Go Down

Topic: Array of strings (Read 730 times) previous topic - next topic

johnmchilton

Jun 06, 2011, 11:12 pm Last Edit: Jun 06, 2011, 11:36 pm by johnmchilton Reason: 1
Hi all,

I'm building a serial communication library. Long story short, I want to declare a variable length array of variable length strings (uniform across the array of pointers, but variable). Here's what I've got:
Code: [Select]

void UComms::init_arg_store(char** arg_store, int max_message_size, int max_param_size) {
 arg_store = (char**) malloc(max_message_size * sizeof(char*));
 
 for(int i=0; i<max_message_size; i++) { // edited from max_param_size
   arg_store[i] = (char*) malloc((max_param_size+1) * sizeof(char*));
 }
}


A computer engineer friend of mine said that using malloc on the Arduino is not preferable if it can be avoided, but I'm not sure how to get around it here.

Furthermore, this code is not working. Does anybody have hints? Thanks.

AWOL

Quote
Does anybody have hints?

Tell us what it is about the code that you think "is not working".
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

johnmchilton

#2
Jun 06, 2011, 11:22 pm Last Edit: Jun 06, 2011, 11:35 pm by johnmchilton Reason: 1
When I use the function body in my setup() function, it works just fine. When I refactor it into a function like this, my program ceases to work.

Normally, it will use the multi-level array in the loop() function by populating it with parsed packetized data from the serial port. But when I use the initialization function instead of putting the code in setup(), the array is populated with an optional seemingly random character followed by a number of backticks (`).

I'm happy to use malloc, really, but I do want the abstraction of this function. I just don't know why it doesn't work this way.

Nick Gammon


Here's what I've got:
Code: [Select]

void UComms::init_arg_store(char** arg_store, int max_message_size, int max_param_size) {
 arg_store = (char**) malloc(max_message_size * sizeof(char*));
 
 for(int i=0; i<max_message_size; i++) { // edited from max_param_size
   arg_store[i] = (char*) malloc((max_param_size+1) * sizeof(char*));
 }
}

Furthermore, this code is not working. Does anybody have hints? Thanks.


Somehow this looks wrong. Certainly it's confusing. You have a pointer to a pointer of type char which you are then treating as an array?

Arguments to functions are passed by value, so I am not expecting that anything this function does will change anything outside the function (except use some of your memory).
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

johnmchilton

#4
Jun 07, 2011, 12:16 am Last Edit: Jun 07, 2011, 12:20 am by johnmchilton Reason: 1
The C++ compiler should treat an array access on to a pointer "properly".

That is, if you have a char** myarray, then
Code: [Select]
myarray[i] should equal
Code: [Select]
*(myarray + i) which will evaluate to the value in memory at myarray + i*sizeof(char).

As for the rest, you're right, I should have known that functions pass by value. So I switched it to:
Code: [Select]
void UComms::init_arg_store(char*** arg_store, int max_message_size, int max_param_size) {
 *arg_store = (char**) malloc(max_message_size * sizeof(char*));
 
 for(int i=0; i<max_message_size; i++) {
   (*arg_store)[i] = (char*) malloc((max_param_size+1) * sizeof(char*));
 }
}


Called by:
Code: [Select]
computer_connection.init(&COMPUTER, COMPUTER_BAUDRATE);
computer_connection.init_arg_store(&arg_store, MAX_MESSAGE_SIZE, MAX_PARAM_SIZE);

And now it works!  :) Thank you both

Nick Gammon

Quote
Code: [Select]
char*** arg_store


Three asterisks, huh? You don't see that very often.

You could have passed it by reference:

Code: [Select]
void UComms::init_arg_store(char** & arg_store, int max_message_size, int max_param_size) {
  arg_store = (char**) malloc(max_message_size * sizeof(char*));
 
  for(int i=0; i<max_message_size; i++) { // edited from max_param_size
    arg_store[i] = (char*) malloc((max_param_size+1) * sizeof(char*));
  }
}


I hope those numbers are small, after all you only have 2048 bytes of RAM, so allocating:

Code: [Select]
(max_message_size * 2) + (max_message_size * max_param_size)

... is going to gobble it up pretty fast.

I don't have a problem with dynamic memory allocation per se, but just remember you don't have a lot of it. And fragmentation could be an issue.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

johnmchilton

Those numbers are indeed very small. They are each 10 at the moment, which is already exorbitantly large for the messages defined in my protocol so far. I don't imagine they need to be more than 4 and 6, respectively.

Yes I could pass by reference, but I didn't know that trick ;) thank you. I learned C from a very hardware-oriented textbook, so I didn't get all the language candy that I would have with K&R (though that's on my bookshelf...).

And finally, I am using an Arduino Mega :) so I am playing fast and loose with my 8KB of memory. I thought about using a sequential array instead of a multi-level array, but I can almost envision the machine code that the compiler generates, and I think that it will put all these blocks close to each other anyway.

Thanks for the pass-by-reference tip!

Aeturnalus

In C/C++, multidimensional arrays are sequential arrays:

int arr[2][2] = {{1,2},{3,4}} is stored as {1,2,3,4}.

johnmchilton

That is true, if you use that syntax. But I decided that I didn't care for a multidimensional array. I wanted a multi-level array, which is different. It is an array of pointers to arrays, which are not necessarily contiguous in memory. Although in this case they probably are because the malloc calls are done one after the other, that is generally not the case, and need not be with a multi-level array.

Go Up