aardvarkk
aardvarkk

Reputation: 15996

Is it possible to pass char[][] to a function requesting char**?

I am trying to call a function that takes char** as a parameter. Its job is to fill an array of strings (i.e. an array of char*). I know the max length of the strings, and I can pass the max number to fill as another parameter, so I was hoping to stack allocate it like this:

fill_my_strings(char** arr_str, int max_str); // function prototype

char fill_these[max_strings][max_chars_per_string]; // allocating chars
fill_my_strings(fill_these, max_strings); // please fill them!

Of course, I get the "cannot convert char[max_strings][max_chars_per_string] to char**" error.

I know this is some subtle (or not-so-subtle) problem with my understanding of the difference between arrays and pointers. I'm just not sure why it's not possible to pass this block of memory to something wanting a char** and have it fill in my stack-allocated chars. Could somebody please explain if this is possible, or if not, why not?

Is it possible to call a function like this without calling malloc / new?

Upvotes: 3

Views: 343

Answers (4)

Dave
Dave

Reputation: 11162

The simple answer to your question is no; a two dimensional array is different than a pointer-to pointer type. Arrays decay to pointers to their first element, but pointers actually are that value.

The difference between these types is clear, if you cast both to char*

int x;
char *arr_pp[] = {"foo", "bar", "baz"};
char arr_2d[][4] = {"foo", "bar", "baz"};

char *cp = (char*)arr_pp;
for(x=0; x<3; x++)
     printf("%d ", cp[x]);
printf("\n");

cp = (char*)arr_2d;  
for(x=0; x<3; x++)
     printf("%d ", cp[x]);
printf("\n");

The output (on my computer) is:

-80 -123 4 
102 111 111 

Where the first row is gibberish formed by the fact that I'm printing an address cast into bytes, and the second row is the ascii values of "foo".

In a function taking a char ** the compiler can't know to decay array types, which don't actually contain pointers.

Upvotes: 3

Shawnone
Shawnone

Reputation: 860

I am not sure why fill_my_strings() need a char** parameter. From your example, caller have already allocated the memory from stack. So using a char* should be OK.

But if you want to use char** or you can't modify the fill_my_strings() function, try following example code:

void fill_my_strings(char** arr_str, int max_chars_per_string, int max_strings)
{

    for(int i = 0; i < max_strings; ++i)
    {
        //Make sure you have enough space
        memcpy(*arr_str, "ABCD", sizeof("ABCD"));

        *arr_str += max_chars_per_string;
    }
}

char fill_these[max_strings][max_chars_per_string];
char* pointer = (char*)fill_these;
fill_my_strings(&pointer, max_strings, max_chars_per_string);

Upvotes: 2

Spidey
Spidey

Reputation: 2589

Suppose you have n pointers to strings of m-1 maximum characters (m characters including the NULL). So, in pure C: sizeof(char[n][m]) will return n*m. sizeof(char**) will return the size of a pointer in your architecture, probably 32 (if x86) or 64 (if x86_64).

char[n][m] actually allocates the n*m byte contiguously. char** allocates a single pointer. This pointer references a memory stripe of *n bytes. Each of these n pointers points to a memory stripe of m characters.

So, considering that sizeof(char) == u, if you declare char a[n][m], when you use a[i][j], the compiler understands *(a + i*m*u + j*u). So, considering that sizeof(char *) == w, if you declare char **a, when you use a[i][j], the compiler understands ((a + i*w) + j*w).

Completely different data management.

The closes thing you could do to handle your special case is to create a char** variable, and populate it with the addresses of your stack allocated matrix.

char **tmp = malloc(max_strings * sizeof(char *));
int i;
for(i = 0; i < max_strings; i++){
    tmp[i] = &(fill_these[i][0]); //you probably can't reference a char[][] with a single index - not confirmed
}

Upvotes: 2

The obvious thing to do is build an index

In c use something like:

char string_list[num_strings][str_length];

// ...

char**index = calloc( (num_strings+1), sizeof(*index) ); // calloc insures NULL termination
for (int i=0; i<num_strings; ++i) {
   index[i] = string_list[i]
}

In c++ prefer new[] to calloc;

Upvotes: 0

Related Questions