Home > front end >  This program sets every row's 3rd element's value to 20. Why doesn't it work?
This program sets every row's 3rd element's value to 20. Why doesn't it work?

Time:08-20

Here's the code that I am trying to work with.

int main(void) {

    int array[2][5];

    for (int ** p = array; p < array   2; p  ) {
        *((*p)   2) = 20;
    }

    for (int row = 0; row < 2; row  ) {
        for (int col = 0; col < 5; col  ) {
            printf("%d ", array[row][col]);
        }
        printf("\n");
    }

    return EXIT_SUCCESS;
}

And this is the output given out by the compiler.

main.c:11:21: warning: initialization of 'int **' from incompatible pointer type 'int (*)[5]' [-Wincompatible-pointer-types]
   11 |     for (int ** p = array; p <  array   2; p  ) {
      |                     ^~~~~
main.c:11:30: warning: comparison of distinct pointer types lacks a cast
   11 |     for (int ** p = array; p <  array   2; p  ) {
      |                              ^
main.exe
make: *** [Makefile:7: main] Error -1073741819

I clearly don't understand type casting when it comes to multidimensional arrays. It would really help if you could explain why this doesn't work.

Omitting the first for loop and doing things manually like this do seem to give out the intended result.

    *((*array)   2) = 20;
    *(*(array   1)   2) = 20;

I can't get the hang of "int (*)[5]".

CodePudding user response:

The type of array is int [2][5], an array of two arrays of 5 int.

When an array is used in an expression other than as the operand of sizeof, the operand of unary &, or as a string literal used to initialize an array, it is automatically converted to a pointer to its first element.

Since the type of array is int [2][5], the type of its first element is int [5], an array of 5 int. So the type of a pointer to its first element is int (*)[5], a pointer to an array of 5 int.

There is no rule that a pointer to an array is converted to a pointer to a pointer. So no further automatic conversion happens. The result of using array in an expression where it is automatically converted is an int (*)[5].

Therefore, p should be declared to be int (*)[5]:

for (int (*p)[5] = array; p < array   2; p  )

CodePudding user response:

A multi-dimensional array in C can be seen as an array of arrays. Each of the two top-level elements in array[2][5] is a one-dimensional array of 5 ints. Like any old array, array "decays" in most expressions to a pointer to its first element, that is, a pointer to an int[5]. A declaration for such a pointer would be int (*firstRow)[5] = array; (initializing it with the address to the first top-level element of array). The parentheses are necessary because the indexing operator [] has higher priority than the dereferencing operator *: int *arrayOf5Pointers[5]; would index first, ad the result would be a pointer, so that it declares a real array (we can index it) whose elements in turn are pointers (we can dereference the result of the indexing). With (*firstRow)[5]; we indicate that we dereference first (the variable is a pointer) and only then index the result of the dereferencing (which must be an array).

The notation for the type of (*firstRow)[5]; (what you would use in a type cast) is, as always, the notation for a declaration without the identifier, hence int (*)[5]. That looks funny and perhaps ambiguous; but Kernighan/Richie said in their C book that the C grammar guarantees that there is only one valid position for an identifier in such a type notation. I believe them.

Your assignment int ** p = array is wrong: You cannot assign a (pointer to an array of 5 ints) to (a pointer to pointer to int). That is true even though you can legitimately access the first element of the two-dimensional array with e.g. int i = **array;: array decays to a pointer to its first element, that is, to a pointer to an array of 5 ints; the first dereferencing results in the actual array of 5 ints; which in turn decays to a pointer to its first int element; which is dereferenced with the second *.

The general takeaway here is: Pointers to arrays are rare and typically unnecessary. Simply index your 2-dimensional array in the normal fashion:

for(int row = 0; row < 2; row  ) { array[row][2] = 20; }

or, better,

for(int row = 0; row < sizeof array/sizeof *array; row  ) { array[row][2] = 20; }

The latter determines the number of top-level elements in the array by dividing its overall size in bytes by the size of its first element. The loop stays correct even if you change the size of array.

  • Related