0

I have been learning C for couple of months and I came across a question which is given below.

#include <stdio.h>

int main() {
    int a[2][2] = {{1,2}, {3,4}};
    int (**p)[2] = &a;

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

    for (int i=0; i<4; ++i) {
        printf("%d %u\n", *(*p + i), (*p + i));
    }

    printf("%u\n", p);
    printf("%u\n", p+1);
    printf("%u\n", p+2);
    printf("%u\n", p+3);
    printf("%u\n", *p);
    printf("%u\n", *p+1);
    printf("%u\n", *p+2);
    printf("%u\n", *p+3);
    puts("");
}

The output that I am getting on my machine is as follows:

1 3751802992
2 3751802996
3 3751803000
4 3751803004
1 1
9 9
17 17
25 25
3751802992
3751803000
3751803008
3751803016
1
9
17
25

I understand the first four lines of the output where the elements of the 2D array and their respective addresses is getting printed but I have absolutely no clue how the other outputs are happening.

I checked in an online IDE and there also I am getting the same output except the addresses which obviously will differ. I know that int (**p)[2] is incomparable pointer type to a[2][2] which is a (int *)[2] data type. But still I want to understand how the p pointer is working.

Can someone please help me understand how this is happening? I have been eagerly waiting to get the logic behind the code outputs. I am extremely sorry for the long code snippet and the long output sequence. Thanks in advance.

N.B - I know that the code is producing a lot of warnings but I want to get the core idea about p.

  • 3
    "I know that the code is producing a lot of warnings" is like saying "sure the house is on fire, but I want to know why the lights are flickering." You've got undefined behaviour. – tadman Mar 30 '21 at 03:33
  • Try watching this one and see if it helps https://youtu.be/zuegQmMdy8M?t=6176 – Mihir Mar 30 '21 at 03:33
  • 1
    At `int (**p)[2] = &a;` it goes off the rails. Everything after that is pure nonsense. You have an array of `int[2][2]`, **not** an array of `int*`. Interpreting integers as actual pointers is asking for trouble. – tadman Mar 30 '21 at 03:34
  • Tip: `*(x+y)` should be written as `x[y]` to make it clear what's going on. I have absolutely no idea what `*(*p + i)` is intending, except perhaps `p[0][i]`? – tadman Mar 30 '21 at 03:36
  • @tadman I am very sorry if I asked a bad question. It's just that I saw this question and I knew that the question is stupid, but I wanted to know how still that output is being processed.. Can you please shed some light on 1 9 17 25 it's as if like they are getting incremented by 8 which is the size of an int. But *p is an address how can it be a small number like 1, 9, 17, ?? Also if I want the 0th element of the array then shouldn't it be ***p? Since p is a double pointer pointing to a array of 2 elements? Again really sorry if I asked a stupid quesition – BongPlayer Mar 30 '21 at 03:47
  • @Mihir thank you so much. I am already watching it. – BongPlayer Mar 30 '21 at 03:49
  • It's not a bad question, but it is asking us to skip past the important lesson here and explain stuff that doesn't have to make any sense. Undefined behaviour *does not have to have an explanation*. There is no reasonable explanation for what happens here unless you want to delve into obscure compiler implementation details. – tadman Mar 30 '21 at 03:51
  • Once you drive the program off a cliff by invoking undefined behaviour then whatever happens past that point is beyond your control. You might crash. You might land on the proverbial wheels and keep going. It's not something you want to do intentionally, and if you're learning, you *must* learn to avoid it. – tadman Mar 30 '21 at 03:53
  • 1
    @tadman Okay I got it. Thank you so much for helping me. – BongPlayer Mar 30 '21 at 03:54
  • 1
    It'd be helpful if you told us where you found this question in the form of a questionable program. Was it in a book? On a web page? What does the book or page say about the program? – Caleb Mar 30 '21 at 03:55
  • @Caleb I found it in a youtube video as a question ..I didn't get their explanation and so I added some extra print statements to see what was going on, which made the matter more worse. Here is the link: https://www.youtube.com/watch?v=r-yyRdUjGt8&t=502s – BongPlayer Mar 30 '21 at 03:58
  • A few links that provide basic discussions of pointers may help. [Difference between char *pp and (char*) p?](https://stackoverflow.com/a/60519053/3422102) and [Pointer to pointer of structs indexing out of bounds(?)...](https://stackoverflow.com/a/60639540/3422102) (ignore the titles, the answers discuss pointer basics) Specific to arrays, see [How to handle a pointer to 2D array [2D array seg fault in C](https://stackoverflow.com/a/60498812/3422102) – David C. Rankin Mar 30 '21 at 04:27
  • @DavidC.Rankin Thanks for the links. They were helpful. – BongPlayer Mar 30 '21 at 05:06
  • 1
    The core idea is this. If the code produces warnings, don't run it. – n. m. could be an AI Mar 30 '21 at 06:07
  • @BongPlayer ^n.m. is absolutely right and not sarcastic. If your code produces warnings - especially without -Wall setting, then it most likely is total nonsense. Running it will do no good. Reasoning about its output even less. – Antti Haapala -- Слава Україні Mar 30 '21 at 06:39

4 Answers4

1

The problem with this code starts right here:

int main() {
    int a[2][2] = {{1,2}, {3,4}};
    int (**p)[2] = &a; // <-- Invalid conversion, undefined behaviour

    // warning: incompatible pointer types initializing 'int (**)[2]' with an expression of type 'int (*)[2][2]' [-Wincompatible-pointer-types]

    // ... Everything past here is undefined behaviour
}

There's a huge difference between int** and what you're attempting to cast, one big enough that this conversion isn't possible.

int** means, specifically, a structure where it's an array of int*, or pointers. Treating int[2] as a pointer is going to be a mess. That any of this code even semi-works is hard to explain. It's the compiler trying to make the best of a bad situation.

tadman
  • 208,517
  • 23
  • 234
  • 262
1

I introduced a macro LEN to calculate your array sizes instead of hard-coping the magic numbers, fixed the declaration of p, changed the unsigned format %u to signed %d as you were printed signed values, the last loop, I am sure what the 2nd thing you were trying to print so left it out, and the last section of print statements were pointers so used %p for those in a loop instead of duplicating the code:

#include <stdio.h>

#define LEN(a) sizeof(a) / sizeof(*a)

int main() {
    int a[2][2] = {{1,2}, {3,4}};
    int (*p)[2] = a;

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

    for (int i=0; i < LEN(a) * LEN(*a); i++) {
        printf("%d\n", *(*p + i));
    }

    for(int i = 0; i < LEN(a) * LEN(*a); i++) {
        printf("%p\n", p + i);
    }

    for(int i = 0; i < LEN(a) * LEN(*a); i++) {
        printf("%p\n", (void *) *(p + i));
    }

    puts("");
}
Allan Wind
  • 23,068
  • 5
  • 28
  • 38
0

This is a problem:

int (**p)[2] = &a; // int (**)[2] = int (*)[2][2]

The type of &a is int (*)[2][2], not int (**)[2]. Your pointer declaration should be

int (*p)[2][2] = &a;

Unless it is the operand of the sizeof or unary & operators or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T will be converted, or "decay", to an expression of type "pointer to T" and the value of the expression will be the address of the first element of the array.

The expression a "decays" from type "2-element array of 2-element array of int" (int [2][2]) to "pointer to 2-element array of int" (int (*)[2]). However, since a is the operand of the unary & operator that conversion doesn’t take place, so &a has type "pointer to 2-element array of 2-element array of int" (int (*)[2][2]). Thus,

   p  == &a
 (*p) ==  a

  (*p) + i  ==   a + i  == &(*p)[i] == &a[i]
*((*p) + i) == *(a + i) ==  (*p)[i] ==  a[i]

  *((*p) + i) + j  ==   *(a + i) + j  == &(*p)[i][j] == &a[i][j]
*(*((*p) + i) + j) == *(*(a + i) + j) ==  (*p)[i][j] ==  a[i][j]
John Bode
  • 119,563
  • 19
  • 122
  • 198
-1

A pointer is used to store the address of variables. So, when we define a pointer to pointer, the first pointer is used to store the address of the second pointer. Thus it is known as double pointers.

EXAMPLE:

int main() {
   int integerValue = 84;
   int *pointer1;
   int **pointer2;
   pointer1 = &integerValue;
   pointer2= &pointer1;
   printf("Value of integer = %d\n", integerValue);
   printf("Value of integer using single pointer = %d\n", *pointer1);
   printf("Value of integer using double pointer = %d\n", **pointer2);
   return 0;
}

OUTPUT:

Value of integer = 84
Value of integer using single pointer = 84
Value of integer using double pointer = 84