Unable to pass array address of type char * [2] to char *** function

My Xcode (should the default compiler be clang?) Shows me a warning on this code:

Incompatible pointer types passing 'char *(*)[2]' to parameter of type 'char ***' (when calling func)

 void func (char ***arrayref, register size_t size) { /// ... } int main() { char *array[2] = {"string", "value"}; func(&array, 2); } 

while this code is not a problem (= no warning):

 void func (char **arrayref, register size_t size) { /// ... } int main() { char *array[2] = {"string", "value"}; func(array, 2); } 

While this removes the warning

 func((char***)&array, 2); 

I still don’t know why the former does not give a warning, while the latter does not.

In addition, when the first problem, I also expect the first one to give a warning:

Incompatible pointer types passing 'char *[2]' to parameter of type 'char **'

+7
c arrays parameter-passing pass-by-reference pointers
source share
4 answers

Time for a brief update on the semantics of the array.

sizeof it is an operand of the sizeof or unary & operators or is a string literal that is used to initialize another array in the declaration, an expression of the type "N-element array of T " will be converted ("decay") to an expression of the type "pointer to T ", and the value of the expression will be the address of the first element in the array.

The expression array in your code is of type "2-element array char * " or char *[2] . When you pass it as an argument to func , as in

 func( array, 2 ); 

the expression is converted to an expression of type "pointer to char * " or char ** , that is, the type that your function expects, and the value of the expression is the address of the first element: array == &array[0] . This is why you are not getting a warning for the second code snippet.

In the first fragment, the array is the operand of the unary operator & , therefore, conversion to the pointer type does not occur; instead, the type of the expression is a "pointer to a 2-element array char * " or char *(*)[2] , which is incompatible with char ** . The value of the address is the same (the address of the first element of the array matches the address of the array itself), but the types do not match, therefore, this is a warning.

So why does it matter? A pointer is just an address, and all addresses are the same, right? Well no. A pointer is an abstraction of an address, with corresponding type semantics. Pointers to different types should not have the same size or representation, and the arithmetic of a pointer depends on the type of the specified type.

For example, if I declare a pointer as char **p; , then the p++ expression will advance the pointer to point to the next object of type char * or sizeof (char *) bytes from the current address. However, if p declared as char *(*p)[2] , the p++ expression will advance p to point to the next two-element char * array, which is 2 * sizeof (char *) bytes from the current address.

+10
source share
 char *array[2] = {"string", "value"}; 

- an array with 2 char * elements.

Using array as an address leads to a pointer to the first element, i. e. type char ** .

Using &array leads to a pointer to the same place, but with type char *(*)[2] (not sure if it is spelled correctly).

This is not the same as char *** - the representation in memory is completely different.

To be more detailed,

 +++++++++++++++++++++++ + array[0] + array[1] + +++++++++++++++++++++++ 

this is an array.

 char ** p1 = array; // is a pointer to the first element, which in turn is a pointer. char *(*p2)[2] = &array; // is a pointer to the whole array. Same address, but different type, ie sizeof(*p1) != sizeof(*p2) and other differences. char ***p3 = &p1; // Now, p1 is a different pointer variable which has an address itself which has type `char ***`. 
+5
source share

Here is an example of how to do what you want and change which array points to:

 char *array2[] = {"string", "NewValue"}; void func0 (char **arrayref, register size_t size) { puts(arrayref[1]); } void func1 (char ***arrayref, register size_t size) { puts(arrayref[0][1]); *arrayref= (char **) array2; } int main() { char *array[] = {"string", "value"}; char **foo = array; func0(foo, 2); func1(&foo,2); func0(foo, 2); } 
+3
source share

You have an array of type char *[2] ie an array of 2 pointers to char . This is a fixed-size array with automatic storage time. The only thing your function can do with this type of array is either use its elements or change them (it cannot change its size or release ... so it makes no sense to try to change the array self ~> in other words: you really don't need a pointer to this array).

Here is a simple example:

 void func (char *arrayref[2]) { printf("%s", arrayref[1]); arrayref[1] = "new value"; } int main() { { char *array[2] = {"string", "value"}; func(array); printf(" -> %s", array[1]); return 0; } 

or alternatively changing func to take an array of indefinite size, which makes it suitable for using char *[X] for any X , and not just 2 (in this case, it already makes sense to pass the size of the array):

 void func (char *arrayref[], size_t size) { if (size > 1) { printf("%s", arrayref[1]); arrayref[1] = "new value"; } } 

anyway, this program will output value -> new value .

If you need your function in order to be able to resize this array or influence the array itself in some other way, you should consider using a dynamically allocated array and passing in char** form.

+2
source share

All Articles