9

I am studying how to display elements of 2D array with the help of pointers. Here is the code I tried:

#include<stdio.h>  

int main(){

    int arr[3][2] = {  

    {7, 8},
    {6,3},
    {3,4}
    };

    printf("%u\n", (arr + 2));
    printf("%u\n", *(arr + 2)); 
}

Output:

6487616
6487616

I am expecting output of *(arr + 2) to be 3. How is it the same as (arr + 2)?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
dave
  • 191
  • 1
  • 7
  • 3
    You need to dereference it twice, since it's a 2D array: `*(*(arr+2))` – Govind Parmar Aug 28 '18 at 16:42
  • @GovindParmar Would you please explain why? – dave Aug 28 '18 at 16:45
  • 4
    `arr[2]` and `*(arr + 2)` are the same by definition in the language (C11 [§6.5.2.1 Array subscripting ¶2](http://port70.net/~nsz/c/c11/n1570.html#6.5.2.1p2)). And you get odd results because you're printing an address, not the value of a cell in the array. That's officially undefined behaviour; you pass an address to `printf()` but tell it that it's got an unsigned integer to work on. You might be OK on a 32-bit platform, sort of, more or less. Results are not remotely reliable on a 64-bit platform. – Jonathan Leffler Aug 28 '18 at 16:45
  • The double dereference is necessary because it is a 2D array. `arr[2]` is a pointer to an array of 2 `int`; `arr[2][0]` and `arr[2][1]` are the two values. `*arr[2]` would reference the `3` too. – Jonathan Leffler Aug 28 '18 at 16:47
  • 1
    technically you're getting UB since `%u` is not for printing pointers. [You have to use `%zu`](https://stackoverflow.com/q/9053658/995714) – phuclv Aug 28 '18 at 16:50
  • 7
    @phuclv: You're right that `%u` is UB, but so is using `%zu` (that's for printing `size_t`). You should either use `%p` and cast the pointer to a `void *`, or use `` and `PRIuPTR` (or `PRIXPTR` for upper-case hex) plus a cast to `uintptr_t`. – Jonathan Leffler Aug 28 '18 at 16:52
  • `printf`-ing pointer values with `%u` triggers undefined behavior. End of story. Your preceived "equivalence" is nothing more than an illusion created by an invalid format specifier in `printf`. – AnT stands with Russia Aug 28 '18 at 17:02
  • @phuclv: How is `%zu` better than `%u`??? `%zu` is intended for `size_t` arguments. Pointers are not `size_t`. – AnT stands with Russia Aug 28 '18 at 17:03
  • @AnT yes that's a typo. The linked question already said that it should be `%p` – phuclv Aug 28 '18 at 17:04
  • 1
    @phuclv : False. `%zu` requires a `size_t` argument. Any incompatible type will trigger undefined behavior. Pointer arguments will trigger undefined behavior. – AnT stands with Russia Aug 28 '18 at 17:04

3 Answers3

14

A 2D array is really an array of arrays.

The expression arr + 2 has type int (*)[2], while *(arr + 2) has type int [2]. When printing the former, you have a pointer so that value of the pointer is printed. In the latter case, you have an array which decays to a pointer to the first element. So *(arr + 2) decays into arr + 2, which is the same as the first expression.

Going into more detail on arr + 2, arr has type int [3][2]. When you add an integer value to it, it decays to a pointer to the first member, so arr decays to type int (*)[2], and arr + 2 also has that type, and points to the subarray containing { 3, 4 }.

Also note that pointers should be printed with the %p format specifier, and that the pointer must be casted to void *, otherwise you invoke undefined behavior. In this case you were "lucky" that they happened to print the same thing.

To get the output of 3 you were expecting, you need to dereference one more time:

*(*(arr + 2))
dbush
  • 205,898
  • 23
  • 218
  • 273
  • `*(*(arr + 2));` throws `[Error] expected ')' before ';' token` but `*(*(arr + 1) + 1));` prints `3` – dave Aug 29 '18 at 05:18
  • 1
    @dave `*(*(arr + 2))` works properly for me. You're probably missing a closing brace for your `printf` call. Also, `*(*(arr + 1) + 1))` is choosing a different array element which happens to have the same value as the one you're attempting to print. `arr[2][0]` and `arr[1][1]` both contain the value 3. – dbush Aug 29 '18 at 11:21
3

arr is an array of arrays of int. On almost any use an array is converted to a pointer to its first element. So arr gets converted to a pointer to an array of int.

OK, arr gets converted to a pointer to an array of int, so (arr+2) is of the same type, that is, a pointer to an array of int.

Now *(arr+2) is the thing (arr+2) points to. That is, an array of int.

Now since it's an array of int, it gets converted to a pointer to its first element. So *(arr+2) gets converted to a pointer to int. Note it is not an int and is unlikely to be equal to 3.

Now how come (arr+2) and *(arr+2) dosplay the same? They are a pointer to an array and a pointer to its first element. Although these pointets are of different types, they represent the same address, because the address of any array is the same as the address of its first element.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
1

arr is the pointer to the first array of the type int[2].(arr + 2) is the pointer to the third such array.
Whereas *(arr + 2) is the pointer to the first element of the (arr +2) array.
Both of these will hence have the same address since they are pointing to the same position.The only difference being in their type.(arr+2) is of the type int(*)[2] whereas *(arr + 2) is of the type int *.

SynAck
  • 427
  • 5
  • 19