38

I’m working on an exercise in K&R (ex. 5–9) and I was trying to convert the original program’s 2D array of

static char daytab[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

into using pointers to an array of 13 ints like

static char (*daytab)[13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

But compiler prints warning: excess elements in scalar initializer.

Googling did not help and even K&R writes when passing the array to a function,

myFunction(int daytab[2][13]) {...}

is the same as

myFunction(int (*daytab)[13]) {...}
Palec
  • 12,743
  • 8
  • 69
  • 138
adelbertc
  • 7,270
  • 11
  • 47
  • 70

3 Answers3

40

The two are only partly equivalent. The difference being that:

static char daytab[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

declares a two-dimensional array, which includes setting aside space for the array and ensuring that daytab references that memory. However:

static char (*daytab)[13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

...only declares a pointer. So you're trying to initialize a pointer with an array initializer, which doesn't work as expected. There is no array; there's no memory set aside for an array. What happens instead is that the first number in your initializer is assigned to the pointer daytab, and the compiler generates a warning to let you know you've specified a lot of additional values that are just discarded. Since the first number in your initializer is 0, you're just setting daytab to NULL in a rather verbose way.

So if you want to do this sort of initialization, use the first version -- it decays to the same pointer type that you explicitly declare in the second version, so you can use it the same way. The second version, with the array pointer, is needed when you wish to dynamically allocate the array or get a reference to another array that already exists.

So you can do this:

static char arr[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
static char (*ptr)[3] = NULL;

ptr = arr;

...and then use ptr and arr interchangeably. Or this:

static char (*ptr)[3] = NULL;

ptr = malloc(2 * sizeof(*ptr));

...to get a dynamically allocated 2-dimensional array (not an array of pointers to 1D arrays, but a real 2D array). Of course, it's not initialized in that case.

The "equivalence" of the two variations just means that the 2D array, when it decays to a pointer to its first element, decays to the type of pointer declared in the second variation. Once the pointer version is actually pointed at an array, the two are equivalent. But the 2D array version sets up memory for the array, where the pointer declaration doesn't... and the pointer can be assigned a new value (pointed at a different array) where the 2D array variable cannot.

In C99 you can do this, though (if not static at least):

char (*daytab)[13] = (char [][13]){
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
Dmitri
  • 9,175
  • 2
  • 27
  • 34
  • Stack space? If it's static then it goes in the data section. – Rag Jan 31 '14 at 19:49
  • @BrianGordon Corrected... I forgot about the "static" while typing that part (I think... it was a long time ago). – Dmitri Jan 31 '14 at 20:40
  • and then the pointer can be accessed like this `(*(daytab + leap))[i]` (with regards to the exercise) – Vladimir Oct 22 '17 at 13:39
8

@Dmitri explained it well, but I wanted to add that

static char (*daytab)[13] = { ... };

is one pointer to an array of 13 char elements. The compiler gives you the warning because you've passed in two arrays. It's like trying to assign two addresses to one pointer char *p = {a, b}. There are more elements than necessary per your declaration. See Geekforgeek's explanation on what an array pointer really means.

As for answering the K&R exercise, consider

Option 1:

static char *daytab[2] = { 
    (char []) {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    (char []) {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    };} 

or Option 2:

static char (*daytab)[13] = (char [][13]) { 
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    };} 

Option 1 is an array of two char pointers.

Option 2 is one array pointer. It points to an array of 13 char elements. Just as you could increment a char pointer to get the next letter in a string, you can increment this array pointer to grab the next array of 13 chars.

khlau
  • 81
  • 1
  • 3
  • `static char (*daytab)[13]` is not a pointer to `char`, it's a pointer to *13-element arrays of `char`*, and can be used to point at an actual 2D array. Your examples with `static char *daytab[2]` are different, with `daytab` being an array of pointers to `char`, each pointed at a 1D array of `char`. With your examples, `daytab` is not a pointer, and the array it refers to contains pointers rather than `char`s. – Dmitri Oct 02 '18 at 23:54
  • Good clarification @Dmitri. I updated my answer a bit. – khlau Oct 17 '18 at 05:24
0

Just worked this problem in K&R myself, so maybe I can add to the really good answers already given. This is a good exercise in getting away from using 2-D arrays and towards using arrays of pointers. Note that at this point in the book we have not been introduced to malloc. So, one approach would be to set up the month arrays beforehand, then an array of pointers to those arrays:

char y0[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char y1[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char *daytab[] = {y0, y1};

Recall that the name of an array is a pointer to the first element. Now you really have an array of pointers to two arrays of 13 ints.

  • Remember that C doesn't really have 2D arrays. You can make an array of 1D arrays which amounts to about the same thing. Except it's n[x][y] instead of n[x,y]. – Alan Corey Mar 15 '21 at 00:51