Reputation: 69
#include <stdio.h>
int main()
{
int a[3][2]={10,11,12,13,14,15};
printf("%d %d",a[2][-2],a[2][-1]);
}
I get the output as 12,13 but I don't understand why? Does a pointer exist to a multidimensional array like a[1] is same as *(a+1) in single dimension??
Upvotes: 2
Views: 101
Reputation: 170203
Your example is officially undefined behavior. I'll refer to the C standard:
Successive subscript operators designate an element of a multidimensional array object. If E is an n-dimensional array (n >= 2) with dimensions i x j x . . . x k, then E (used as other than an lvalue) is converted to a pointer to an (n - 1)-dimensional array with dimensions j x . . . x k. If the unary * operator is applied to this pointer explicitly, or implicitly as a result of subscripting, the result is the referenced (n - 1)-dimensional array, which itself is converted into a pointer if used as other than an lvalue. It follows from this that arrays are stored in row-major order (last subscript varies fastest).
When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i-n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.
The emphasis above is mine. The expression a[2][-2]
carries a lot of meaning per the above two paragraphs. According to the first paragraphs a[2]
will refer to an array, specifically the third int[2]
contained in a
. At this point, any subscript operator applied further needs to be valid in regards to this array of 2 integers.
Since [-2]
is now applied to an int[2]
, the resulting pointer arithmetic goes outside the aggregate it's applied to, and according to the second paragraph, is undefined behavior.
Having said that, most implementations I'm aware of do the thing one may expect, and the other answers document well how it is that you got those values.
Upvotes: 2
Reputation: 38
Your array looks like this:
10 | 11
---+---
12 | 13
---+---
14 | 15
but in the memory it looks like this:
10
--
11
--
.
.
.
--
15
That means that if you want to access the element a[1][1]
(= 13) you don't have to access the 1 + 1 = 2nd element because you have to skip the entire first row with 2 elements so you have to access the 1 * 2 + 1 = 3rd (10 would be the 0th) element.
So if you have an array:
int a[nRows][nCols];
accessing a[i][j]
would look like this:
*(a + nCols * i + j) = val;
So in your example it would be:
*(a + 2 * 2 + (-2)) = *(a + 2)
and
*(a + 2 * 2 + (-1)) = *(a + 3)
Upvotes: 0
Reputation: 35154
I don't think that it is undefined behaviour; The C standard at paragraph 6.5.2.1 Array subscripting describes how array subscripting is interpreted. I applied the paragraph defined there to the example asked by the OP:
int a[3][2];
Here a
is a 3 × 2 array of ints; more precisely, a
is an array of three element objects, each of which is an array of two ints. In the expression a[i]
, which is equivalent to (*((a)+(i)))
, a
is first converted to a pointer to the initial array of two ints. Then i
is adjusted according to the type of a
, which conceptually entails multiplying i
by the size of the object to which the pointer points, namely an array of two int objects. Then, an expression a[i][j]
, is equivalent to (*(*((a)+(i)))+(j))
, where j
conceptually is multiplied by the size of int.
In terms of memory addresses, and assuming 4
as the sizeof(int)
, this means a + (i*sizeof(int[2])) + (j*sizeof(int))
; for i=2
and j=-2
this means a + 16 - 8
, i.e. a + 2*sizeof(int)
. In the memory layout of int a[3][2]
, dereferencing this memory address achieves 12
.
Upvotes: 0
Reputation: 311088
A two-dimensional array declared like
int a[3][2] = { 10, 11, 12, 13, 14, 15 };
you can interpret as a one dimensional array declared like
int a[3 * 2] = { 10, 11, 12, 13, 14, 15 };
Between indices of the two-dimensional array and indices of the one-dimensional array there is the following relation
a[i][j]
corresponds to a[2 * i + j]
So the element a[2][-2]
of the two dimensional array corresponds to the element a[2 * 2 - 2]
of the one-dimensional array that is to a[2]
The value of a[2]
is equal to 12
. And the value of the element a[2][-1]
of the two-dimensional array corresponds to the value of the element a[2 * 2 - 1]
of the one-dimensional array that is to a[3]
equal to 13.
And the reverse calculation. If you have an element of a one-dimensional array a[i] then it has the following corresponding element of a two-dimensional array
a[i / nCols][i % nCols ]
where nCols
is the number of columns in the two-dimensional array.
Upvotes: 1