Search code examples
c

Multidimensional arrays in c - problem with pointers


void g()
{
   int a[3][2] = {{1,2},{3,4},{5,6}};
   printf("%d\n", *(a[2]-1));
}

I need to figure out the output of the function (written in c)

I expected it may fail or print some random stuff, because a[2] is a pointer to an array, and a[2]-1 will point to some random area in the memory. However, the answer is that it prints 4 - I can not figure about why it does that. Any ideas?


Solution

  • C evaluates *(a[2]-1):

    1. Since a is not the operand of sizeof, the operand of unary &, or a string literal used to initialize an array, it is automatically converted to a pointer to its first element. As a is an array of 3 arrays of 2 int, its first element is an array of 2 int, which we usually think of as a[0]. So the conversion to a pointer to this yields &a[0].
    2. For a[2], the subscript operator is defined to add the integer to the pointer and then apply *, as if we had written * (&a[0] + 2). Adding 2 to &a[0] produces a pointer to two elements further in the array a, namely the element a[2], so the produced pointer is &a[2]. Then applying * yields a[2]. (Yes, this is the same thing we started with in this step—I am showing the semantics as specified by the C standard and that they produce the result we intuitively think of.)
    3. In a[2]-1, a[2] is an array (it is an array of 2 int), and it is not the operand of sizeof, the operand of unary &, or a string literal used to initialize an array. So it is automatically converted to a pointer to its first element. We usually think of its first element as a[2][0], so the pointer is &a[2][0].
    4. Then for the -, we subtract 1 from the pointer &a[2][0]. This points to an element back one element in the array a[2].

    At this point, we have a problem. Subtracting one from &a[2][0] would produce &a[2][-1], but that is outside the bounds of a[2]. We know the element prior to a[2][0] in memory is a[1][1], because arrays (and their subarrays) are laid out in memory contiguously. And thus, when we apply * to this pointer, a C implementation will often produce the value of a[1][1], which is 4.

    However, pointer arithmetic is not defined by the C standard when it goes out of bounds of the array the initial pointer points to. Those bounds are the starting element of the array and a position just beyond the last element. For a[2], the bounds are from &a[2][0] (its start element) to &a[2][2] (one beyond its last element, a[2]][1]). &a[2][-1] is not in those bounds.

    Some C implementations may support crossing subarray boundaries within a larger array. However, it is also possible that an optimizer in the compiler handles such a crossing differently and produces a different result.