1

I was testing some codes to find out how 2d array is implemented in c. Then I met following problem.

The code is:

int main(){
   int a[4][4];
   printf("a: %p, *a: %p, **a: 0x%x\n",a,*a,**a);
}

I compiled this with 32-bit ubuntu gcc

The result was:

a: 0xbf9d6fdc, *a: 0xbf9d6fdc, **a: 0x0

I expected different value for a and *a, but they are same.

  1. why a and *a are same in this case? Is not a a int** type?

  2. Then what is role of *operator in *a?

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
DongRyeong
  • 21
  • 2
  • 1
    *"Is not `a` a `int**` type?"* In no case whatsoever is there an `int **` in *any* of this. Wherever you read/inferred/were-told that, they're wrong. – WhozCraig May 22 '19 at 05:26
  • 1
    Note you should technically cast the arguments corresponding to a `%p` formatter to `void*`. On most architectures, passing any pointer type will have the same effects, but this is not guaranteed. – aschepler May 22 '19 at 06:05
  • 1
    Doing `**a` may invoke undefined behaviour as it evaluates to `a[0][0]` which has not been initialised/set before. – alk May 22 '19 at 06:21
  • 1
    @alk Pedantically, `a` has its address taken and cannot be declared with `register` qualifier. On mainstream systems without exotic trap representations, this means that the variable takes an unspecified value, but it is not UB. https://stackoverflow.com/a/40674888/584518 – Lundin May 22 '19 at 06:44

1 Answers1

7

Check the types!!

Given the definition as int a[4][4];

  • a is of type int [4][4] - array of an array of 4 integers. It's not the same as int **.
  • a[n] is of type int [4] - array of 4 integers. It's not the same as int *
  • a[n][m] is of type int. - integer.

Now, given the fact, that the address of the array is also the address of the first element in the array, the values are same, but they differ in types.

To check it visually

int a[4][4]

      +-------+--------+-------+-----------+
      |       |        |       |           |
      |a[0][0]| a[0][1]| a[0][2]   a[0][3] |
      |       |        |       |           |
      +------------------------------------+
      |       |        |       |           |
      |a[1][0]|        |       |           |
      |       |        |       |           |
      +------------------------------------+
      |       |        |       |           |
      |a[2][0]|        |       |           |
      |       |        |       |           |
      +------------------------------------+
      |       |        |       |           |
      |       |        |       |           |
      |       |        |       |           |
      |       |        |       |           |
      +-------+--------+-------+-----------+

Then, quoting the C11, §6.3.2.1

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. [...]

So, while passing an argument of type array as a function argument, it decays to the pointer to the first element of the array.

So, let's have a look.

  • a, decays to &(a[0]) - the address of the first element of type int (*)[4].
  • *a, which is the same as a[0], decays to an int *, pointer to the first element.
  • **a which is same as *(*a) == *(a[0]) == a[0][0] - that's the int value at that index.

Now once again look carefully at the image above - do you see that the first element of int [0] and int [0][0] are basically residing at the same address? In other words, the starting address are the same.

That's the reason behind the output you see.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261