views:

144

answers:

3

I have the following code:

int main() {
    int n = 3, m = 4, a[n][m], i, j, (* p)[m] = a;
    for (i = 0; i < n; i++)
        for (j = 0; j < m; j++)
            a[i][j] = 1;
    p++;
    (*p)[2] = 9;
    return 0;
}

I have a hard time understanding what p is here, and the consequences of the operations on p in the end. Can someone give me a brief explanation of what happens. I know c-pointers in their simple settings, but here it get slightly more complicated.

+2  A: 

a is an array of int[m], and p is a pointer to int[m]. It's initialized to a, which decays to a pointer to its first element.

Dereferencing p yields int[m], which decays to int* (pointer to its first element). So (*p)[2] adds 2 to the int* that was the result of the decay. So it's setting the third of the first 4 integers (int[m], with m being 4) to 9.

Adding to p would advance the address stored in it by units of sizeof(int[m]) bytes, which at runtime (since m is not a compile time constant) evaluates to 4 * sizeof(int). So (*(p+1))[2] would access the third of the second 4 integers. In total, p has 3 such arrays of 4 ints to point to: a+0, a+1 and a+2.

Notice that you created a C99 VLA, variable length array. You need to #define n and m to integer constants to get rid of those VLAs (which aren't quite portable among C compilers).

Johannes Schaub - litb
A: 

p is a pointer to a 4-element int arrays (i.e. a pointer to pointer to int, where the first dimension is 4 and the second is unknown). When you increment p, it points to the next 4-element int array, i.e. the fifth int altogether. Then p is dereferenced with offset 2, which means that the seventh int changes, so you get

  1 1 1 1
  1 1 9 1
  1 1 1 1

as the final state of the 4x3 array.

Kilian Foth
+2  A: 

Some small print statements should make clear what this code does:

#include <stdio.h>

int main() {
    int n = 3, m = 4, a[n][m], i, j, (* p)[m] = a;
    for (i = 0; i < n; i++)
        for (j = 0; j < m; j++)
            a[i][j] = 1;

    printf("p points to %u, sizeof(int)*m = %d", p, sizeof(int)*m);
    p++;
    printf(", p++ points to %u\n", p);

    (*p)[2] = 9;

    for (i = 0; i < n; i++)
        for (j = 0; j < m; j++)
            printf("a[%d][%d] = %d\n", i, j, a[i][j]);

    return 0;
}

Example output:

p points to 2293536, sizeof(int)*m = 16, p++ points to 2293552
a[0][0] = 1
a[0][1] = 1
a[0][2] = 1
a[0][3] = 1
a[1][0] = 1
a[1][1] = 1
a[1][2] = 9
a[1][3] = 1
a[2][0] = 1
a[2][1] = 1
a[2][2] = 1
a[2][3] = 1

Clearly, p is a pointer to int[m], so p++ will advance the pointer to the next row in a. That is why (*p)[2] will change a[1][2]. If you didn't increment p, that line would change a[0][2].

Hope that was simple enough!?

AndiDog