#include "stdio.h" int main(void) { int a[3][2] = {{10,2}, {2,3}, {3,4}}; printf("%p\n", a[0]); printf("%p\n", (int *)a); printf("%p\n", *a); printf("%p\n", a); printf("%p\n", &(a[0][0])); return 0; } 

The code gives the same number ... It is particularly striking that a and * a are equal. What's happening? And when I look at * (int *) a, I get 10 ...

  • The multidimensionality of arrays in C is an abstraction. In reality, this is only indirect addressing. - 0andriy
  • @ 0andriy: And what multidimensionality would not be an "abstraction"? What is the inferiority of "indirect addressing" as a natural way of implementing multidimensionality? - AnT
  • @Ant, any that is implemented appropriately in the hardware. Regarding the second question, I do not understand your motivation. You probably attribute something to me about which I did not speak, and then you are trying to refute my alleged statement. Maybe a couple of days off will not hurt;) - 0andriy
  • @ 0andriy I do not attribute anything, I just ask a simple question. It is still not clear to me which implementation will be considered “properly implemented”. Are you saying that a multidimensional iron is needed for a multidimensional array or something else? (This is also just a question, no "attribution") - AnT
  • I do not understand why such a question arose, but I do not know the answer to it. You can debate outside of this post, I think. - 0andriy

3 answers 3

You should not be surprised by the equality of a and *a in this context. There is nothing surprising in this. In the C language, a “two-dimensional array” is simply an ordinary one-dimensional array, whose elements are also one-dimensional arrays.

A one-dimensional array of, say, double d[10] is simply a flat, continuous, compact memory block, of size 10 * sizeof(double) , consisting of ten closely related double elements. At the same time, the address of the whole array &d coincides numerically with the address of its zero element &d[0] , because they “start” at the same point of memory. The situation is completely analogous to the equality of pointers in this example.

 struct S { int a; } s; printf("%p %p\n", (void *) &s, (void *) &s.a); // Оба указателя совпадают численно 

The address of the entire object of the struct type is numerically the same as the address of the very first field inside this object. Quite similarly, the address of the entire array is numerically the same as the address of the very first (zero) element of this array.

At the same time, in the C language, the expression d type " double [10] array double [10] " in most contexts (not all) is implicitly reduced to type double * - a pointer pointing to the zero element of the array. Those. in most contexts, the expression d behaves equivalently to the expression &d[0] , i.e. array looks like a pointer outwardly. This phenomenon is called array type decay . (It is for this reason that arrays in C are often confused with pointers, although in reality they are different types and there are no pointers in arrays.)

All this is immediately applicable to two-dimensional arrays (and multidimensional arrays), because, as mentioned above, in C, a “two-dimensional array” is a structure that is recursive in its structure: it is simply a one-dimensional array, whose elements are one-dimensional arrays. The above array type decay phenomenon works the same at any level of this recursion.

The above expression a initially has the type int [3][2] , but in this context it is implicitly cast to the type "pointer to the zero element of the array a ". This pointer is of type int (*)[2] and points to a subarray a[0] type int [2] as part of a .

The expression *a initially has the type int [2] - this is the subarray a[0] in the composition of a , but in this context *a implicitly reduced to the type "pointer to the zero element of the array a[0] ". This is an int * type pointer that points to the very first int in the a[0] subarray, i.e. on a[0][0] .

Both a , and a[0] , and a[0][0] begin at the same memory point, for which reason the numeric representations of these pointers are the same

 printf("%p %p %p\n", (void *) &a, (void *) a, (void *) *a); // Все три указателя совпадают численно 
  • Thank you very much! - George Kasparyants

Take an int type of four bytes


Let's imagine an array in memory:

  +------------+----------+----------+----------+----------+ | Переменная | a[0][0] | a[0][1] | a[1][0] | a[1][1] | +------------+----------+----------+----------+----------+ | Адрес | 0x61fe98 | 0x61fe9c | 0x61fea0 | 0x61fea4 | +------------+----------+----------+----------+----------+ | Значение | 10 | 2 | 2 | 3 | +------------+----------+----------+----------+----------+ 

In the debugger you can see:

 print &a => (int (*)[3][2]) 0x61fe98 // указатель на двумерный массив print a => {{10, 2}, {2, 3}, {3, 4}} print &(*a) => (int (*)[2]) 0x61fe98 // указатель на одномерный массив print *a => {10, 2} print &(**a) => (int *) 0x61fe98 // указатель на целое число print **a => 10 

As you can see, the addresses of the two-dimensional array a , one-dimensional subarray a[0] ( {10, 2} ) and the first element of the subarray a[0][0] ( 10 ) are equal. I.e:

 &a == &(*a) == &(**a) ^ ^ ^ | | | a[0][0] (первое значение первого подмассива двумерного массива) | | | | a[0][] (первый подмассив двумерного массива) | | a[][] (весь двумерный массив) 

So:

 printf("%p\n", a[0]); // 1. адрес первого подмассива двумерного массива (int (*)[2]) 0x61fe98 printf("%p\n", (int *)a); // 2. адрес двумерного массива, просто // приведенный к указателю, имеет тип (int*), // если разыменовать его получим 10 printf("%p\n", *a); // 3. то же самое, что и 1 printf("%p\n", a); // 4. не то же самое, что и 2! Имеет тот же // адрес, но тип (int (*)[3][2]) printf("%p\n", &(a[0][0])); // 5. Указатель на первый элемент подмассива // двумерного массива, то же самое что и 2 
  • Then, if a [i] = * (a + i) - why do we get a pointer to a [i] [0]? - George Kasparyants
  • I want to say, if a is a pointer to the first element of the array, it turns out * a = 10 should be, but it turned out that * a = a - George Kasparyants
  • @Eanmos, * a - this is what lies at address a, right? It turns out at this address should be a pointer to the beginning of the array, on the other hand, as you said, a points to the beginning of a two-dimensional array, and I even checked * (int *) a == 10. That's what confused me ... That is on the one hand, at address a, there is a pointer, on the other hand, at address a, the first element of the array should lie, and everything depends on the conversion of pointer types ... - George Kasparyants
  • @GeorgeKasparyants, updated the answer. - eanmos
 printf("%p\n", a[0]); // <- 1 printf("%p\n", (int *)a); // <- 2 printf("%p\n", *a); // <- 3 printf("%p\n", a); // <- 4 printf("%p\n", &(a[0][0])); // <- 5 

In order to understand what is happening, it is useful to understand what an array is in reality. In C / C ++, an array is a regular pointer to the beginning of an array. The values ​​in the array are stored sequentially. Also on the pointers there is arithmetic . That is, if we add 1 to the pointer, then we get an indicator for the next element of this type. So what is the record a[i] in fact, this record is equivalent to the following: *(a+i) that is, we take the value that is stored in the i -th cell after the one pointed to by a . And in fact, such a record is also valid: i[a] (try, for example, output 0[a] ). So a[0] = *(a+0) = *a . Thus equality 1 and 3 established.
Now let's look at why a and *a same. In fact, because they point exactly in the same place. These 6 numbers will be stored in memory in sequence: {10, 2, 2, 3, 3, 4} . Thus, a indicating the entire array and a[0] pointing to the piece {10, 2} will point to the same place, since their beginnings coincide.
Also a will be of type int (*)[2] . Thus, despite the fact that a and *a same, but a+1 and *a+1 will no longer be the same, since they indicate types of different sizes. And the shift by 1 for these pointers will be different.
Now about the rest: a is a pointer, so when converting to a pointer to an int (( int *)a ), it will continue to point to the same cell. And finally: &(a[0][0]) = &(*(a[0]+0)) = a[0]+0 = a[0] since a[0][0] is zero the element in this array and its address is the address of the beginning of the entire array.

  • Forgot about number 5. &(a[0][0]) == &(a[0]) == &a . - eanmos
  • I want to say, if a is a pointer to the first element of the array, it turns out * a = 10 should be, but it turned out that * a = a - George Kasparyants
  • "In C / C ++, an array is a regular pointer to the beginning of an array." - this is absolutely not true. In C and C ++, an array is not a pointer, but only implicitly given in it (in a number of specified contexts). It is a completely different matter. - AnT