To answer the first question, the principles might be clearer if we use a single depth of pointer. This code is illegal for the same reason:
int *ptr = { 1, 2, 3 };
In C, a braced initializer list is not an object (especially not an array). It can only be taken as a list of items from which to read initializers when an object is being initialized.
ptr
is one object, so at most one initializer could be taken, and the expected form of that initializer is a pointer (which 1
is not).
In fact this code is explicitly illegal under C11 6.7.9/11:
The initializer for a scalar shall be a single expression, optionally enclosed in braces
However, there is a gcc bug/feature where it permits excess initializers for a scalar and ignores them. Further, some compilers may "be helpful" and "only" issue a warning, and initialize ptr
to point to address 1
, wherever that might be.
"scalar" means an object that's not a struct or an array.
Since C99 you can write:
int *ptr = (int []){1, 2, 3};
which creates an array (using the same storage duration as ptr
) and points ptr
at its first element.
This array is mutable; for a non-mutable one use int const *ptr = (int const[]){1, 2, 3};
instead.
Replacing int
by char *
, we see that you could write:
char **p = (char *[]){ "123", "456", "789" };
in which case the pointers in the array are mutable, but the things they point to (i.e. the string literals) still aren't.
Note that you should always use char const *
when dealing with string literals, because they are not mutable. The fact that string literals have type char [N]
is a historical hangover from before const
was added to C. So:
char const **pp = (char const *[]){ "123", "456", "789" };
or with non-mutable pointers to strings:
char const *const *pp = (char const *const []){ "123", "456", "789" };