0

I was exploring typedefs and stumbled upon this program

EDIT: Nearly all the answers were concerned about the warnings it generates. So, I went ahead and removed all the warnings but the question remains the same.

#include<stdio.h>
typedef int int3[3];
int main(){
    int a[2][3] = {{1,2,3}, {4,5}};
    int3 *p = a;
    int *ip = (int *) a;
    printf("sizeof:\np: %lu\n(*p+0): %lu\n**p: %lu\nip: %lu\n*ip: %lu\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
    printf("---\n");
    printf("p: %p\tp+1: %p\n*p: %p\t*p+1: %p\n**p: %d\nip: %p\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
    return 0;
}

on one run it shows:

sizeof:
p: 8
(*p+0): 8
**p: 4
ip: 8
*ip: 4
---
p: 0x7ffe36df31b0   p+1: 0x7ffe36df31bc
*p: 0x7ffe36df31b0  *p+1: 0x7ffe36df31b4
**p: 1
ip: 0x7ffe36df31b0
*ip: 1

My question is: If p and *p are equal
and **p = 1
then why not *p = 1 as well ?

Provided size of pointer = 8 and size of int = 4
Even if taking in account pointer arithmetic, then *p should at least be 0xkk kk kk kk 00 00 00 01
k being any hex number (consider little-endian)
because de-referencing the same address as int should give 1

EDIT: To make things clearer, consider this table:

+-----------+----------------------+------------------+
|Variable   | Value                | Address          |
|           |                      |                  |
|           |                      |                  |
| p         | 0x7ffe36df31b0       |                  |
|           |                      |                  |
|*p         | 0x7ffe36df31b0       |  0x7ffe36df31b0  |
|           |                      |                  |
|**p        | 1                    |  0x7ffe36df31b0  |
+-----------+----------------------+------------------+

How can *p and **p have same address but different value ?

Hritik
  • 534
  • 4
  • 18
  • 2
    please compile with warnings enabled and then edit your code to remove the warnings – jhnc May 01 '19 at 23:42
  • 2
    P is pointing at a 2 dimensional array. Essentially a pointer to a pointer. So * p gives you the address of the array {1,2,3}, p[0], while ** p is nearly equivalent to p[0][0]. – 138 May 01 '19 at 23:48
  • 1
    Don't use `%x` to print the value of a pointer, use `%p`. – Barmar May 02 '19 at 00:04
  • Also `*p + 0` is nonsense. Or it isn't but doesn't tell anything – Antti Haapala May 02 '19 at 04:58
  • @AnttiHaapala `*p`, by itself represents an array. As per rules, `sizeof` operator doesn't convert the array name to a pointer. `+0` turns it into a pointer. – Hritik May 05 '19 at 20:09
  • @138 If *p gives the address of array {1,2,3} to which it points, then why does p also give the same address ? Doesn't p point to a multi D array ? – Hritik May 05 '19 at 20:13

3 Answers3

2

First, note that this code in a confusing mish-mash of constraint violations combined with array decay to generate output of questionable usefulness. But I'll try to answer your question as to what's going on, and try using mundane explanations without resorting to citations from the C Standard, as others already have answered in that manner.

How can *p and **p have same address but different value ?

Because p is a pointer to an array.

Given

typedef int int3[3];
int3 *p;

that means *p is a three-dimensional array. Note that

int3 *p = a;

is a constraint violation, as noted elsewhere. You can force that to "work" with a cast, but it's still fundamentally wrong and the code only "gets away" with it because the address of an array is the address of the first element of that array - and your current implementation doesn't differentiate the pointer types in a significant-enough fashion they they're fundamentally incompatible. Pick an implementation where a plain int * is, say, a 32-bit offset value while an array would have as its address both a 32-bit segment and a 32-bit offset value, and the code would likely generate nonsense if it didn't fail utterly. That's why the C Standard requires pointers to be to "compatible types" - because pointers don't have to be compatible at all.

That problem aside, the initialization/assignment on your implementation can seemingly be treated as

int3 *p = ( int3 * ) a;

Since that "works", that means p contains the address of a three-element array of int values. So dereferencing p with *p is the same as a[0], or the first three-element int array in the two-dimensional a array.

So what is *p, then?

It's exactly the same in this case as a[0], an array. And such a bare array reference decays to an address*, and that address is of the same type as a pointer to an element of the array. And the value of that address will be that of the address of the first element of the array.

So *p, which is a three-element array of int, can itself be dereferenced and evaluate to the first element of a[0], or a[0][0]. So **p is the same as a[0][0].

And a[0][0] - or **p - is an int that is initialized with the value of 1.

While *p is a[0] and the address of that first element.

* - A lot of people say an array "decays to a pointer" or "an array is a pointer", but I like "decays to an address" because a pointer can be assigned to but an address simply is - it can't be assigned to itself, just like an array itself can't be assigned to, but the address can be assigned to something else, like a pointer. Note than when an array is passed to a function, the address of the array actually is passed as an actual pointer. The pointer is the function's parameter and that function parameter can be assigned to...

Andrew Henle
  • 27,654
  • 3
  • 23
  • 49
1

the posted code, when compiled with:

gcc -c -Wall -Wextra -Wconversion -pedantic -std=gnu11 

results in the following compiler messages:

gcc    -ggdb -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled.c"  (in directory: /home/richard/Documents/forum)
untitled.c: In function ‘main’:
untitled.c:6:15: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
     int *ip = a;
               ^
untitled.c:7:26: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                         ~^
                         %ld
untitled.c:7:38: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                     ~^
                                     %ld
untitled.c:7:47: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                              ~^
                                              %ld
untitled.c:7:55: warning: format ‘%d’ expects argument of type ‘int’, but argument 5 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                                      ~^
                                                      %ld
untitled.c:7:64: warning: format ‘%d’ expects argument of type ‘int’, but argument 6 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                                               ~^
                                                               %ld
untitled.c:9:17: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int (*)[3]’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                ~^
untitled.c:9:26: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘int (*)[3]’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                         ~^                                                ~~~
untitled.c:9:34: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                 ~^
                                 %ls
untitled.c:9:44: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 5 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                           ~^                                     ~~~~
                                           %ls
untitled.c:9:61: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 7 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                                            ~^
                                                            %ls
Compilation finished successfully.

The compiler finishes up by saying the compilation was successful. That does NOT mean that it is ok to run this code and expect valid results/output.

When you correct the code so it cleanly compiles, then please post a EDIT to your question that contains the modified/corrected code.

gcc    -ggdb -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled.c"  (in directory: /home/richard/Documents/forum)
untitled.c: In function ‘main’:
untitled.c:6:15: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
     int *ip = a;
               ^
untitled.c:7:26: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                         ~^
                         %ld
untitled.c:7:38: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                     ~^
                                     %ld
untitled.c:7:47: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                              ~^
                                              %ld
untitled.c:7:55: warning: format ‘%d’ expects argument of type ‘int’, but argument 5 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                                      ~^
                                                      %ld
untitled.c:7:64: warning: format ‘%d’ expects argument of type ‘int’, but argument 6 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                                               ~^
                                                               %ld
untitled.c:9:17: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int (*)[3]’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                ~^
untitled.c:9:26: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘int (*)[3]’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                         ~^                                                ~~~
untitled.c:9:34: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                 ~^
                                 %ls
untitled.c:9:44: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 5 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                           ~^                                     ~~~~
                                           %ls
untitled.c:9:61: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 7 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                                            ~^
                                                            %ls
Compilation finished successfully.
user3629249
  • 15,593
  • 1
  • 16
  • 17
0

What you have here is constraint violation because you are using incompatible pointer types in the following initialization:

int *ip = a;

As per C11 standard section on Pointer declarators

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

and on Simple assignment/Constraints

the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;

The compiler is require to emit a diagnostic in this case which it does:

<source>:8:15: warning: initialization of 'int *' from incompatible pointer type 'int (*)[3]' [-Wincompatible-pointer-types]

     int *ip = a;

See Demo

When you compile a program, it is good practice to enable all warnings and treat warnings as errors so as to avoid (as far as possible) this kind of behavior.

P.W
  • 24,743
  • 6
  • 32
  • 69
  • The behaviour is not undefined *there*, but it is constraint violation, so a compiler **must** issue a diagnostics message. – Antti Haapala May 02 '19 at 04:46
  • That's what I thought too at first. But found the quote in the Annex. Please let me know if my interpretation of the quote is wrong. – P.W May 02 '19 at 04:49
  • The annex is not normative. In addition, there is a clause [C11 5.1.1.3p1](http://port70.net/~nsz/c/c11/n1570.html#5.1.1.3p1) *" A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, **even if the behavior is also explicitly specified as undefined or implementation-defined.**"* – Antti Haapala May 02 '19 at 04:51
  • [6.7.9p11](http://port70.net/~nsz/c/c11/n1570.html#6.7.9p11) says that the semantic rules of initialization are the same as for simple assignment and simple assignment says in constraints that the pointers must be compatible – Antti Haapala May 02 '19 at 04:55
  • Thank you. I have revised the answer as per your inputs. Just curious to know: If the annex on undefined behavior is not normative , does it mean that the compiler can make some of the points listed there *defined* behavior? In that case, is the compiler required to document it? – P.W May 02 '19 at 05:11
  • Does that mean conversion between pointers is not defined by the language ? – Hritik May 05 '19 at 20:07
  • @Hritik yes, it is defined, It is defined that you must use a *cast* (explicit conversion) – Antti Haapala May 06 '19 at 01:40