0

Consider the following C program:

#include <stdio.h>
#include <stdlib.h>
int main(){
    char c[1] = {'Q'};
    printf("%c ",*(char*)(c));   // line 1
    printf("%c\n",*(char*)(&c));  // line 2
}

the output is Q Q

Here is my understanding of what should happen, c is a pointer to a char, so the char printed by line 1 should be the letter Q because the pointer to a char is being cast as a pointer to a char (so nothing happens) then it is dereferenced. Because the char that c points to is 'Q', line 1 prints 'Q'. This seems to make sense to me.

However line 2 does not. The address of c is cast as a pointer to a char so what I believe should happens is after being dereferenced the expression *(char*)(&c) should simplify to the value of the pointer c but expressed as a char.

These both give the same result and I don't think it is a coincidence because I've tried it on many different letters. I'd like to know why this is. Thanks

PS:
I tried this:

#include <stdio.h>
#include <stdlib.h>
int main(){
    char c[10] = "asdf";
    printf("%c ",*(char*)(c));   // line 1
    printf("%c\n",*(char*)(&c));  // line 2
}

and I got this: a a

Weather Vane
  • 33,872
  • 7
  • 36
  • 56
Mathew
  • 1,116
  • 5
  • 27
  • 59
  • Hint: `c` is an array. – Sourav Ghosh Jun 14 '18 at 16:28
  • I am aware of that but still confused, I suspect this has to do with strings but I don't understand how – Mathew Jun 14 '18 at 16:29
  • Try doing it with an array of length 5 and I think you'll see something different. ;) – J. Doe Jun 14 '18 at 16:32
  • @J.Doe please try that ;) – Weather Vane Jun 14 '18 at 16:38
  • Ok, I've done it, I'll explain more in the question – Mathew Jun 14 '18 at 16:39
  • take a look [here](http://www.c-faq.com/aryptr/aryvsadr.html). For more info on the difference between pointers and arrays, read [all of them](http://www.c-faq.com/aryptr/index.html) – yano Jun 14 '18 at 16:41
  • So I was expecting to get the whole string on the second one based on @achal's answer because I'm now thinking the second one is a pointer to a string however I got the same in both cases – Mathew Jun 14 '18 at 16:42
  • @WeatherVane Okay, now I'm confused. Shouldn't dereferencing the location of a pointer just return the pointer, thus print the whole string? I guess casting it to a `char*` instead of a `char**` somehow already sort of derefrenced it once?... – J. Doe Jun 14 '18 at 16:44
  • I edited the initialisation because `char c[10] = {'asdf'};` generates a compiler warning about conversion between types and because it didn't give the output you say. – Weather Vane Jun 14 '18 at 16:47
  • thanks for that edit @WeatherVane, that is in fact the way I ran the code, I just typed it into the question wrong – Mathew Jun 14 '18 at 16:49
  • Possible duplicate of [In C, are arrays pointers or used as pointers?](https://stackoverflow.com/questions/4607128/in-c-are-arrays-pointers-or-used-as-pointers) – n. m. could be an AI Jun 14 '18 at 16:53
  • @J.Doe `c` is not a pointer but an array. The array `c` ***decays*** to a pointer when used without an index. If you have `char a; char *c = &a;` then the *value* of `c` is very different from its *location* `&c`. Also "print the whole string?" No, the format specifer is `%c`. – Weather Vane Jun 14 '18 at 17:00

2 Answers2

3

In the statement

char c[1] = {'Q'};

c is char array & array name i.e c itself represents base address of that array. If you prints c and &c both gives the same results.

Side Note :- c means its a pointer to the first elements of array and &c means its a pointer to the whole array.

int main(void){
        char c[1] = {'Q'};
        printf("%p %p\n",(void*)c,(void*)&c); /* bot results the same */
        return 0;
}

Thats why both *(char*)(c) and *(char*)(&c) yields in same results. for e.g

char c[10] = "asdf"; /* lets assume base address of c is 0x100 */

It looks like

 --------------------------------------
 |  a   |   s   |   d   |  f   | \0   |
 --------------------------------------
0x100   0x101   0x102 ..
c

Next how these two expression *(char*)(c) & *(char*)(&c) executed.

*(char*)(c)  =>  *(char*)(0x100)  => typecasted as char* means c points to 1 byte memory 
             =>   *(0x100)        => value in the first byte from 0x100 to 0x101 => a

and

*(char*)(&c)     =>  *(char*)(&(0x100)) => *(char*) (0x100) => c and &c are same 
                 =>   *(0x100)          => value in the first byte from 0x100 to 0x101 => a
Achal
  • 11,821
  • 2
  • 15
  • 37
  • 1
    Nitpicking: you should cast at least &c to (void*) before priting it with "%p". – Petr Skocik Jun 14 '18 at 16:35
  • could you give more details? I'm still confused – Mathew Jun 14 '18 at 16:37
  • 1
    Agree @PSkocik I forgot to add that. Thanks for pointing that – Achal Jun 14 '18 at 16:52
  • @fred which part you didn't understood ? – Achal Jun 14 '18 at 16:53
  • why are c and &c the same? my understanding of pointers is they store a value (a pointer to a place in memory) and they have their own location in memory, so if you do &c you get the location in memory of c but if you do c you get the value stored at c – Mathew Jun 14 '18 at 16:53
  • @fred that's the property of array. Array name itself address, hence `c` and `&c` are same. I shown one example above in my answer where `c` and `&c` both are `0x100`. – Achal Jun 14 '18 at 16:55
  • ok, it seems weird to me but if that's just how arrays work then I guess that fine, I'll accept your answer, thanks a lot for the help – Mathew Jun 14 '18 at 16:58
  • @fred The name of an array `c` usually evaluates to the address of the first element of the array `&c[0]` , so `c` and `&c` have the same value. But note that `array` and `pointer` are different. I hope you understood now – Achal Jun 14 '18 at 16:58
0

If you think about the equality (void*) c == (void*) &c, you would realize that it is logically correct. Take for instance a static allocated integer:

int myvar = 2;

Printing the value of myvar makes sense, since it's an integer and it's "universally" recognizable.

Now how can you identify an array? Obviously with an address to that memory storage.

But how can you print it? There is not a rule for that. What's the point on distinguishing between c and &c if there is not a reason to do this. One could speculate that c can be interpreted as the first element of an array, but you can imagine all the drawbacks of this choice.

The reasoning of your compiler is actually different, especially because variables do not exist for it, so it replaces them with something it can work with.

Take for example this snippet:

int a = 2;
char c[6] = {'a', 'b', 'c', 'd', 'e', '\0'};

printf("%p\n",  c);
printf("%p\n", (void*) &c);
printf("%d\n", a);
printf("%p\n", (void*) &a);

Here is the generated assembly that gcc produces (intel syntax):

    mov     DWORD PTR [rbp-4], 2     # int a = 2;
    mov     BYTE PTR [rbp-16], 97    # char c[6] = {'a', 'b', 'c', 'd', 'e', '\0'};
    mov     BYTE PTR [rbp-15], 98
    mov     BYTE PTR [rbp-14], 99
    mov     BYTE PTR [rbp-13], 100
    mov     BYTE PTR [rbp-12], 101
    mov     BYTE PTR [rbp-11], 0
    lea     rax, [rbp-16]            # printf("%p\n",  c);
    mov     rsi, rax
    mov     edi, OFFSET FLAT:.LC0
    mov     eax, 0
    call    printf
    lea     rax, [rbp-16]            # printf("%p\n", (void*) &c);
    mov     rsi, rax
    mov     edi, OFFSET FLAT:.LC0
    mov     eax, 0
    call    printf
    mov     eax, DWORD PTR [rbp-4]   # printf("%d\n", a);
    mov     esi, eax
    mov     edi, OFFSET FLAT:.LC1 [complete object constructor] [complete object constructor]
    mov     eax, 0
    call    printf
    lea     rax, [rbp-4]             # printf("%p\n", (void*) &a);
    mov     rsi, rax
    mov     edi, OFFSET FLAT:.LC0
    mov     eax, 0
    call    printf

The compiler interprets both c and &c in the same way.

Marco Luzzara
  • 5,540
  • 3
  • 16
  • 42