Passing an array of strings as parameter to a function in C
Solution 1
The warning is exactly right. Your function wants an array of pointers. You're giving it an array of arrays.
Expected:
sep_foo: +------+ +-----+ |char**|--> 0: |char*|-->"string1" +------+ +-----+ 1: |char*|-->"string2" +-----+ *sep_foo_qty-1: |... | +-----+
What you provided:
sep_foo: +--------------------------------+ 0: | char[MAX_STRING_LENGTH] | +--------------------------------+ 1: | char[MAX_STRING_LENGTH] | +--------------------------------+ MAX_QTY-1: | ... | +--------------------------------+
An array with elements of type X
can "decay" into a pointer-to-X
, or X*
. But the value of X
isn't allowed to change in that conversion. Only one decay operation is allowed. You'd need it to happen twice. In your case, X
is array-of-MAX_STRING_LENGTH
-chars. The function wants X
to be pointer-to-char. Since those aren't the same, the compiler warns you. I'm a bit surprised it was just a warning since nothing good can come from what the compiler allowed to happen.
In your function, you could write this code:
char* y = NULL;
*sep_foo = y;
That's legal code since sep_foo
is a char**
, so *sep_foo
is a char*
, and so is y
; you can assign them. But with what you tried to do, *sep_foo
wouldn't really be a char*
; it would be pointing to an array of char. Your code, in effect, would be attempting to do this:
char destination[MAX_STRING_LENGTH];
char* y = NULL;
destination = y;
You can't assign a pointer into an array, and so the compiler warns that the call is no good.
There are two ways to solve this:
Change the way you declare and allocate
sep_foo
on the calling side so it matches what the function expects to receive:char** sep_foo = calloc(MAX_QTY, sizeof(char*)); for (int i = 0; i < MAX_QTY; ++i) sep_foo[i] = malloc(MAX_STRING_LENGTH);
or, equivalently
char* sep_foo[MAX_QTY]; for (int i = 0; i < MAX_QTY; ++i) sep_foo[i] = malloc(MAX_STRING_LENGTH);
Change the prototype of the function to accept what you're really giving it:
int parse(const char *foo, char sep_foo[MAX_QTY][MAX_STRING_LENGTH], int *sep_foo_qty);
Solution 2
Parameter 2 should be
char sep_foo[][MAX_STRING_LENGTH]
To clarify, you are passing a pointer to parse() and treating it as a pointer to a pointer. A multidimensional array in C is NOT an array of pointers. It is a single block of memory that is pointed to by the array variable. You cannot dereference it twice.
Solution 3
sep_foo
is defined as an array of arrays. In other words, when you use sep_foo
, it points to the beginning of sequential memory. Here's a model:
(assume MAX_STRING_LENGTH = 16, MAX_QTY = 2)
sep_foo = &&0000
sep_foo[0] = &0000
sep_foo[0][0] = *&0000 = 12
sep_foo[0][8] = *&0008 = 74
sep_foo[1] = &0010
sep_foo[1][0] = *&0010 = 12
0000 12 34 56 78 9A BC DE F0 74 10 25 89 63 AC DB FE
0010 12 34 56 78 9A BC DE F0 74 10 25 89 63 AC DB FE
However, your function expects an array of pointers (actually, a pointer to a pointer). This is modeled as such:
sep_foo_arg = &&0000
sep_foo_arg[0] = *&&0000 = &0010
sep_foo_arg[0][0] = *&*&0000 = 12
sep_foo_arg[0][8] = *(&*&0000 + 8) = 74
sep_foo_arg[1] = *&&0002 = &0020
sep_foo_arg[1][0] = *&*&0000 = 12
0000 0010 0020 xxxx xxxx xxxx xxxx xxxx xxxx
0010 12 34 56 78 9A BC DE F0 74 10 25 89 63 AC DB FE
0020 12 34 56 78 9A BC DE F0 74 10 25 89 63 AC DB FE
Yeah ... Syntax may be a bit confusing for my explanations...
Anyway, you can solve this issue by telling your function how to treat the pointer pointed to. In particular, you would want to treat it as an array (a sequence of memory):
int parse(const char *foo, char (*sep_foo)[MAX_STRING_LENGTH], int *sep_foo_qty);
mmutilva
Updated on May 15, 2020Comments
-
mmutilva almost 4 years
I want a simple function that receives a string and returns an array of strings after some parsing. So, this is my function signature:
int parse(const char *foo, char **sep_foo, int *sep_foo_qty) { int i; char *token; ... strcpy(sep_foo[i], token); /* sf here */ ... }
Then I call it like this:
char sep_foo[MAX_QTY][MAX_STRING_LENGTH]; char foo[MAX_STRING_LENGTH]; int sep_foo_qty, error; ... error = parse(foo, sep_foo, &sep_foo_qyt); ...
This way I get a warning during compilation:
warning: passing argument 2 of 'parse' from incompatible pointer type
And then a segmentation fault during execution in the line marked with /* sf here */
What is wrong in my C code?
Thanks in advance
-
mmutilva over 15 yearsI don't need to allocate memroy for char *token as I'm using as the result of strtok_r function: token = strtok_r(copied_foo, "/", &save_ptr); ... token = strtok_r(NULL, "/", &save_ptr); Thanks
-
strager over 15 years@che, An array can be converted to a pointer by taking the pointer of its first element (ptr = &array[0]). This can be done implicitly (ptr = array). The reverse is not true.
-
strager over 15 yearsAlso, when you take the pointer of an array (ptr = &ar), ptr contains the address of the array as a whole. ptr would then be equal to &ar[0], in general, except the type is different. If you took the pointer of a pointer, you would get a number which, is not equal to that pointer (memory is ref'd).
-
strager over 15 yearsOn your edit... You did a much better job at a diagram than I did. I'm the opposite of artistic. =]
-
che over 15 yearsI've been trying to understand strager's memory models, but you've nailed the difference in three clear and simple sentences. Thanks for the clarification!
-
Johannes Schaub - litb over 15 yearsRob, i like you answer very much. i also like how that answer nicely complements my recent pet peeve: stackoverflow.com/questions/423823/… . I will put a link on that to your answer. have fun :)
-
Johannes Schaub - litb over 15 yearsstrager, didn't you mean char (sep_foo)[MAX_STRING_LENGTH] ? because as it is now, it's still a "char*". looks like a typo to me.
-
strager over 15 years@litb, Ah, right. Normally I look this up, but I was in a hurry when I wrote it (couldn't you tell? =]). Thanks.
-
strager over 15 years@litb, Wait -- did YOU make a typo? I don't know which one is right any more. @_@ Some testing is in order later.
-
Johannes Schaub - litb over 15 yearsi'm sorry i see i sounded confusing. well i mean you made teh typo :) look: T f[N] is an array of T. T (*f)[N] is a pointer to an array of T (what you wanted). the * binds more to the T than to the parameter name, so you have to paren it to the f, so that f becomes a pointer.
-
Johannes Schaub - litb over 15 yearsbecause if you say "T *f[N]", it is the same thing as "T **f" in the parameter list. i don't believe you meant that.
-
Johannes Schaub - litb over 15 yearsthis is why the other guy could write "T f[][some_number]": the "[]" is just dropped, and the resulting parameter is a pointer to that thing: "T f[some_number]" => "T (*f)[some_number]". for one dimension might be clearer: "T f[]" = "T f" => "T *f". instead of [] u can write [X]: X is ignored.
-
mmutilva over 15 yearsMoreover, the explaination is very good, but you didn't provide the correct code to use as codelogic did.
-
strager over 15 yearsNot sure exactly why it is char (*f)[n] and not char *(f[n]). Wait ... now I see (while typing this)! Thanks for clearing that up, litb!
-
mmutilva over 15 yearsRob, I'm actually the one who asked the question in the first place. You are right, I did not asked 'how to fix it' i thought it was implicit ;). What I wanted was both things: a good explination as yours and the solution. I still don't understand why you talk about sep_foo_ty insetead of sep_foo
-
mmutilva over 15 yearsIn my code sep_foo_qty is just an int * i use to return the large of the resulting array sep_foo (because the int returned by the function is an error code). Pleas clarify your answer with respect to that and if you also put the correct code this will become the accepted answer ;)
-
Incerteza over 9 yearsDoes
char (*sep_foo)[MAX_STRING_LENGTH]
mean "an array of the pointers to char"? Are the parenthesis needed there? -
Rob Kennedy over 9 yearsOK, @Alexander. I'm going to delete the comments I've made today and start over. Which part of my answer was unclear and prompted you to have to ask your question?
-
Incerteza over 9 yearsThe part that says "Your function wants an array of pointers", whereas it wants "char **sep_foo".
-
Rob Kennedy over 9 yearsThe
char**
argument in this function expects to receive an array of pointers, @Alexander. More precisely, it should be a pointer to an array of pointers, but there's no other way to pass such a thing, so the first level of pointer is implicit. Specifically, the pointers in the array are meant to point at arrays ofchar
. The picture illustrates exactly that. -
Incerteza over 9 yearsWhy an array of pointers? It expects to receive a pointer to a pointer of char, doesn't it?
-
Rob Kennedy over 9 yearsBecause the question says so, @Alexander. The function is declared to receive a pointer to a pointer to
char
, and its description is such that the first pointer tochar
may be followed by additional pointers tochar
, thus yielding an array. What did you expect instead? Perhaps you're not realizing that pointer-to-X and pointer-to-array-of-X are represented identically?