maxgonz
maxgonz

Reputation: 15

Difference between *[] and ** in this code block?

First code block (*arr[])

char *arr[] = {"abc", "def"};

int main(void) {
    int arr_len = sizeof(arr) / sizeof(*arr);
    printf("%d\n", arr_len);

    return 0;
}

Second code block (**arr)

char **arr = {"abc", "def"};

int main(void) {
    int arr_len = sizeof(arr) / sizeof(*arr);
    printf("%d\n", arr_len);

    return 0;
}

Compiler warnings for second code block:

second.c:3:15: warning: initialization of ‘char **’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types]
 char **arr = {"abc", "def"};
               ^~~~~
second.c:3:15: note: (near initialization for ‘arr’)
second.c:3:22: warning: excess elements in scalar initializer
 char **arr = {"abc", "def"};
                      ^~~~~
second.c:3:22: note: (near initialization for ‘arr’)

For the first block, I get the expected output of 2 as there are only two strings in the array. For second block however, I get 1 as the output.

Since arrays decay into pointers, isn't **arr the same?

Upvotes: 0

Views: 227

Answers (1)

Eric Postpischil
Eric Postpischil

Reputation: 222763

char **arr = {"abc", "def"}; defines arr to be a pointer to a pointer to a char. Note that it is a pointer—just one. So it should be initialized with one pointer, and that should be a pointer to a pointer to char.

{"abc", "def"} is a list of two things, so it is wrong for initializing one thing.

"abc" is a string literal. Used in this way, it is automatically converted to a pointer to its first element. That results in a pointer to char, which is the wrong type to initialize a pointer to a pointer to char.

You can provide a single pointer of the correct type by using a compound literal:

char **arr = (char *[]) { "abc", "def" };

That form, that looks like a cast (but is not) in front of a list, creates an object of the type given in parentheses and initializes it with the things inside braces. So the code above creates an array of char * and initializes them with the string literals (which are converted to char *). Then that array is automatically converted to a pointer to its first element, and that pointer initializes arr.

Once you have done this, however, sizeof(arr) / sizeof(*arr); will be the size of a char ** (because that is what arr is) divided by a char * (because that is what *arr is).

In contrast, char *arr[] = {"abc", "def"}; defines an array of pointers to char and initializes it with the two pointers that result from the string literals. That simpler definition is usually what one wants.

Since arrays decay into pointers, isn't **arr the same?

The fact that an array used in an expression (other than as the operand of sizeof or unary &) is converted to a pointer does not mean the array is a pointer. An array is an array. The address of the array is where the array is (the same, except for type, as the address of its first element)—it is not the address of a pointer.

Upvotes: 4

Related Questions