I am able to do this:
char * months[] = {\"empty\",\"jan\",\"feb\",\"mar\",\"apr\",\"may\",\"jun\",\"jul\",\"aug\",\"sep\",\"oct\",\"nov\",\"dec\"};
unsigned sh
Type Controls Everything
char *months[] = {"empty","jan",...};
Declares months as an array-of-pointers to char
where the initializer completes the type providing the number of pointers in the array (the "How many?" information) and initializing each pointer to point to the beginning of a valid character string. The complete type is char *[13]
(an array of 13 pointers.
If you simply tried to declare:
char *months[];
You compiler would throw an error complaining that months
was in incomplete type.
After months
has been declared and initialized, then by operation of C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3), on access, months is type-compatible with char**
as the first level of indirection is converted to a pointer to the first element, resulting in months being char**
on access (subject to the 4 exceptions in the rule).
Why Can't I Just Declare char **months = {"empty","jan",...};
To Begin With?
With a pointer-to-pointer-to-type there is no array involved at all. How many pointers are declared? While with char *months[]...
the initializer for the array can complete the type and provide for the number of elements in an array, with char **months
, you simply have a (pointer)-to-pointer with NO array involved at all. Meaning following the declarations of:
char **months;
months
is simply an uninitialized pointer holding some indeterminate address. Any attempt to change the content of the indeterminate memory location results in Undefined Behavior (and likely a SegFault). The C-standard simply does not provide for initialization of a pointer-to-pointer as it does for an Array. C11 Standard - 6.7.9 Initialization
It fails to build with a
excess elements in scalar initializer
. What does that mean exactly, and how would I initialize the above using a pointer to pointers?
The error, while a bit subtle, makes perfect sense. You have a single pointer when you declare:
char **months;
If you try to initialize the single pointer with an initializer-list, e.g.
char **months = { "empty", "jan", "feb", ... };
There a are whole lot more than 1 excess element in the initializer...
You have two ways to initialize a pointer-to-pointer-to-type. (1) You can either allocate the number of pointers you need, e.g.
char **months = malloc (13 * sizeof *months); /* allocate 13 pointers */
and then allocate for, or assign, a valid address to the first character in a character string to each of the 13-pointers, e.g. months[0] = "empty"; months[1] = "jan", ...
; or
(2) you can assign the valid address of a (single) object that is type-compatible with your pointer, e.g.
char **my_months = months;
You can then access each individual string with my_months[0]
, because the pointer-to-pointer now points to valid storage and the type is complete such that my_months + 1
points to the next string after the current. For example:
#include
int main (void) {
char *months[] = { "empty", "jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec" };
char **my_months = months;
size_t n = sizeof months / sizeof *months;
for (size_t i = 0; i < n; i++)
printf ("my_months[%2zu] : %s\n", i, my_months[i]);
}
Example Use/Output
$ ./bin/my_months
my_months[ 0] : empty
my_months[ 1] : jan
my_months[ 2] : feb
my_months[ 3] : mar
my_months[ 4] : apr
my_months[ 5] : may
my_months[ 6] : jun
my_months[ 7] : jul
my_months[ 8] : aug
my_months[ 9] : sep
my_months[10] : oct
my_months[11] : nov
my_months[12] : dec
Look things over and let me know if you have further questions.