Array of strings

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:

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.

Does anybody have hints?

Tell us what it is about the code that you think "is not working".

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.

johnmchilton:
Here's what I've got:

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).

The C++ compiler should treat an array access on to a pointer "properly".

That is, if you have a char** myarray, then myarray[i] should equal *(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:

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:

computer_connection.init(&COMPUTER, COMPUTER_BAUDRATE);
computer_connection.init_arg_store(&arg_store, MAX_MESSAGE_SIZE, MAX_PARAM_SIZE);

And now it works! :slight_smile: Thank you both

char*** arg_store

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

You could have passed it by reference:

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:

(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.

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 :wink: 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 :slight_smile: 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!

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

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

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.