3

I have read many posts of pointers and 2d array relation, but I cant seem to understand concept. Lets say there is a 2d array int a[3][2] and an array int b[2].
now a is returning a pointer to array of integers of size 3. It would be of type int (*)[2].

As my understanding of the concept goes derefrencing it(*a) would give me the array itself and this decays to a pointer pointing to first element of the 1d array and is of type (int*) . now the bold part is the doubt.

why and how does this decay happen where the array itself(which is the complete 1d array a[0]) decays to first element of the 1d array?
(cant seem to get the why and how part) and cant seem to find it on other links also.

As a[0], *a,&a[0][0] represent the same pointer. here as a[0] is the first 1d array. then is this true that b(declared above) and a[0] represent the same thing (both being 1d array and then decay to the pointer to first element in array of type (int*)?

CRABOLO
  • 8,605
  • 39
  • 41
  • 68
user rk
  • 376
  • 5
  • 13

5 Answers5

2

why and how does this decay happen where the array itself decays to first element of the 1d array?

C11: 6.3.2.1 p(3):

Except when it is the operand of the sizeof operator, the _Alignof operator 1,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.

In simple; the array names can be converted to pointer to its first element.

Since a is an array name, it decays to the pointer to its first element which is a 1D array. Its type is int(*)[2]. Since a (after decay) is a pointer to an array of 2 int, *a is an array of 2 ints. Since that's an array type, i.e *a is an array name for the first 1D array, it decays to a pointer to the first element (a[0][0]) of the array object. So, its type is int *.

is this true that b(declared above) and a[0] represent the same thing (both being 1d array and then decay to the pointer to first element in array of type (int*)?

Yes b and a[0] are both of type int * (after decay).


1. Read the commat by Keith Thompson. Also read this answer which states that: A draft of the C11 standard says that there's another exception for arrays, namely when the array is the operand of the new _Alignof operator. This is an error in the draft, corrected in the final published C11 standard; _Alignof can only be applied to a parenthesized type name, not to an expression.

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 2
    What is "the decay convention" that you quote? It isn't the C standard, source needed. – Lundin Jun 09 '14 at 13:36
  • @Lundin; By the way here is that [decay convention](http://www.ibiblio.org/pub/languages/fortran/append-c.html) (non-standard): *"The famous "decay convention": an array is treated as a pointer that points to the first element of the array."* – haccks Jun 09 '14 at 13:50
  • @haccks are b and a[0] both of same type before decay also? – user rk Jun 09 '14 at 14:36
  • @haccks cant get it you have written correctly in the above comment i think that a is pointer to an array of 2 ints(as stated by you in the answer) and *a or a[0] is an array of 2 ints(array type) which decays to pointer to first element . (so as b is array of 3 ints(array type) as stated by you in the above comment) then it infers that a[0] and b are of same type(array type). this is what i infer from your answer and part of your above comment. then with what i have inferred from the answer i cant seem to fully understand your above comment. – user rk Jun 09 '14 at 15:10
  • 1
    @haccks (but i do know that pointers and arrays are not same only pntr arthmtic & array indexing are equivalent giving rise to these properties) – user rk Jun 09 '14 at 15:12
  • @userrk; OK. Now I understand what you want to know. Yes. `b` and `a[0]` are of same type. Both are 1D arrays. – haccks Jun 09 '14 at 15:40
2

The answer to your "why " question is: because the language specification says so. Arrays always decay to pointers to their first elements (aside from a number of specific contexts). So, as you correctly noted *a has type int [2]. This is an array. And since it is an array, it is required by the language to decay to pointer of type int * that points to (*a)[0] (again, with the exception of a few specific contexts). Your original a is an array of type int [3][2] that decays to int (*)[2] pointer. Meanwhile *a is an array of type int [2] that decays to int * pointer. That's why.

As for "how", it is not clear what exactly you are asking about. "Decay" in this case means that in expressions values of array type are implicitly replaced by values of pointer type. That's all there is to it.

And yes, both a[0] and b in your example are arrays of type int [2], which decay identically to pointers of type int *. I.e. they are "the same thing" with regard to decaying behavior.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
1

The rule specifying this conversion is hidden in the C11 chapter 6.3.2:

...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.

Meaning if you have an expression where the array name b of int b[3] is used, it will get converted to a pointer to int, an int*.

This rule is generic. If you have an array int a[3][2], then formally you have a "size 3 array of size 2 arrays of type int". This will get converted to a a pointer to type, which is a "pointer to array of size 2 of type int", int(*)[2].

Note that the "decay" only goes "down" one level, so by using the two-dimensional array name a, you will always get an array pointer and not a pointer to the first element. The difference between an array pointer and a plain int pointer is mainly stylistic, to keep the language consistent. If you print the actual address of the pointer as an integer, it will be the same no matter pointer type.

so as my understanding of the concept goes derefrencing it (*a) would give me the array itself and this decays to a pointer pointing to first element of the 1d array and is of type (int*)

It will give you the array itself, which will decay into an array pointer. It will not decay any further from there, just one level. So *a will not give you a pointer to the first element.

why and how does this decay happen where the array itself decays to first element of the 1d array?

Whenever you use the array name inside an expression it will decay, from "array to type" to "pointer to type". If "type" happens to be an array, you get a pointer to an array.

and one more doubt i had. As a[0], *a, &a[0][0] represent the same pointer. here as a[0] is the first 1d array.

a[0] and *a are equivalent, both gives an array pointer. &a[0][0] is a pointer to int. They have different types but in practice points at the same memory.

Lundin
  • 195,001
  • 40
  • 254
  • 396
1

Try the following code:

int x[3][10];
printf("%d\n", x[0]);
printf("%d\n", x[1]);
printf("%d\n", x[2]);

Look at the following expression

if(&x[0][9] + 1 == &x[1][0])
    printf("I've entered here");

This evaluates to true... When you add 1 to the pointer it is increasing its address by 4 (or whatever int size your machine has), showing that in memory, element x[1][0] comes right after element x[0][9]

an integer has size 4, you will see that the difference between the prints is 40 (4 * 10) elements...

This array was allocated at compilation time so it already knows how much space it needs and it allocates space for 3*10 integers contiguous, the same way as it would be if it was declared as

 int x[30];

when you use the name x alone what you get is the address of the first element it points to...

The second dimension is just for us programmers... But it comes at a small cost for the compiler... when you access like

 x[1][8];

it has to calculate how much it has to add to the start address to access that element,

which would be (1 * 10 + 8) * SIZE_OF_ARRAY_TYPE (which is int here) ...

In the above expression 1 = the number on the first dimension, multiplied by 10 (number of elements in the second dimension that would be on first row) added to the number of elements in the second []

and if you had a 1 dimension array:

 x[23]

the only account is 23 * SIZE_OF_ARRAY_TYPE (which is int here)

you can also try this IF statement here:

 if(&x[0][10] == &x[1][0])
    printf("I've entered here");

or even assignments such as

x[1][4] = 20;

printf("%d\n", x[0][14]);

Pointers give you a lot of power, but with a great power comes a great responsibility ;)

vmp
  • 2,370
  • 1
  • 13
  • 17
  • This causes undefined behaviour; `printf("%p\n", (void *)x[0]);` would be the correct approach. – M.M Jun 10 '14 at 02:22
  • if you want to see the number in base 16... %d prints in base 10 – vmp Jun 10 '14 at 02:29
  • `%d` causes undefined behaviour unless you pass an `int`. You could write `printf("%d\n", (int)x[0])`, or preferably `printf(PRIuPTR "\n", (uintptr_t)x[0]);` There is no implicit conversion of pointers when used as arguments matching the `...` part of a prototype. – M.M Jun 10 '14 at 02:31
  • a pointer holds an integer value that represents the address it is pointing to – vmp Jun 10 '14 at 02:33
  • "a pointer holds an integer value" - no, a pointer holds a pointer value. Pointer types are different to integral types. See C99 6.2.5 for definition of *type*, and 7.19.6.1#8 for requirements on the argument corresponding to `%d`. – M.M Jun 10 '14 at 02:36
  • both visual studio and gcc didn't report any error... in what compiler are you having trouble to compile that? – vmp Jun 10 '14 at 02:39
  • Compilers aren't required to diagnose undefined behaviour. In this case, the undefined behaviour happens at runtime when the statement is executed. It sounds like you are not familiar with undefined behaviour, [here](http://stackoverflow.com/questions/949433/why-are-these-constructs-undefined-behavior?rq=1) is another example with some further discussion. [Here](http://stackoverflow.com/questions/1632080/printf-specify-integer-format-string-for-float?rq=1) is another thread about printf format specifier mismatch. – M.M Jun 10 '14 at 02:49
  • I'm not saying that anyone should be using %d with a pointer... but I've never found any compiler failing to print it... I never wanted to know the real address, only the 'distance' between two addresses, which is much easier for us to read if it is in base 10... I don't care about the undefined, as long as x[0] gives me UNDEFINED and x[1] gives UNDEFINED + 4 (which has always been the case).... – vmp Jun 10 '14 at 02:52
  • "undefined behaviour" means anything could happen. Please do some reading instead of making stuff up, it doesn't mean "the value printed is undefined" or something. The program could crash, or it could format your hard drive, or so on. Just because you never encountered that personally does not mean that it never happened to anyone else. The whole point of having standards is that they specify what is defined and what isn't. If you want to reliably output the addresses then do what I suggested; if you want to roll the dice then stick with what you are doing. – M.M Jun 10 '14 at 02:58
  • More commonly, if run on a big-endian system with 64-bit pointers and 32-bit ints, your version is likely to output the same number 3 times – M.M Jun 10 '14 at 03:00
  • ok, so in your manual there is it illegal to do (x[1] - x[0]) when x has 2 dimensions, and using that as an integer? – vmp Jun 10 '14 at 03:16
  • `x[1] - x[0]` has type `ptrdiff_t` and the right specifier is `%td`. The `ptrdiff_t` is an integral type but it might not be exactly `int` (and won't be on I32LP64 for example). Again, either use `%td`, or cast to `(int)`. – M.M Jun 10 '14 at 03:19
  • well, I tried to find the session 7.19.6 but the manual I found ended in chapter 6. Did I get the wrong one? – vmp Jun 10 '14 at 03:40
  • [PDF here](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf), and for other related documents http://stackoverflow.com/questions/81656/where-do-i-find-the-current-c-or-c-standard-documents – M.M Jun 10 '14 at 03:43
1

The array int a[3][2] is stored in memory as 1d array in row major order, so this array:

int a[3][2] = {
  {1, 2},
  {3, 4},
  {5, 6}
};

will be stored as:

int a[] = {1, 2, 3, 4, 5, 6};

for instance:

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char **argv)
{
  size_t i;

  int a[3][2] = {
    {1, 2},
    {3, 4},
    {5, 6}
  };

  for(i = 0 ; i < sizeof(a) / sizeof(int) ; i++)
  {
    printf("%d\n", ((int *) a)[i]);
  }

  return 0;
}