58

I was trying to understand something with pointers, so I wrote this code:

#include <stdio.h>

int main(void)
{
    char s[] = "asd";
    char **p = &s;

    printf("The value of s is: %p\n", s);
    printf("The direction of s is: %p\n", &s);

    printf("The value of p is: %p\n", p);
    printf("The direction of p is: %p\n", &p);

    printf("The direction of s[0] is: %p\n", &s[0]);
    printf("The direction of s[1] is: %p\n", &s[1]);
    printf("The direction of s[2] is: %p\n", &s[2]);

    return 0;
}

When compiling it with gcc I get these warnings:

$ gcc main.c -o main-bin -ansi -pedantic -Wall -lm
main.c: In function ‘main’:
main.c:6: warning: initialization from incompatible pointer type
main.c:9: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char (*)[4]’
main.c:11: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char **’
main.c:12: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char ***’

(The flags for gcc are because I must be C89)

Why incompatible types of pointer? Isn't the name of an array a pointer to it's first element? So if s is a pointer to 'a', &s must be a char **, no? And why do I get the other warnings? Do I have to cast the pointers with (void *) in order to print them?

And when running I get something like this:

$ ./main-bin
The value of s is: 0xbfb7c860
The direction of s is: 0xbfb7c860
The value of p is: 0xbfb7c860
The direction of p is: 0xbfb7c85c
The direction of s[0] is: 0xbfb7c860
The direction of s[1] is: 0xbfb7c861
The direction of s[2] is: 0xbfb7c862

How can the value of s and it's direction (and of course the value of p) be the same?

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
alcuadrado
  • 8,340
  • 3
  • 24
  • 25

8 Answers8

37

"s" is not a "char*", it's a "char[4]". And so, "&s" is not a "char**", but actually "a pointer to an array of 4 characater". Your compiler may treat "&s" as if you had written "&s[0]", which is roughly the same thing, but is a "char*".

When you write "char** p = &s;" you are trying to say "I want p to be set to the address of the thing which currently points to "asd". But currently there is nothing which points to "asd". There is just an array which holds "asd";

char s[] = "asd";
char *p = &s[0];  // alternately you could use the shorthand char*p = s;
char **pp = &p;
James Curran
  • 101,701
  • 37
  • 181
  • 258
  • an array also means a contiguous memory address of particular type since `s` decays to a pointer, we could say it's a pointer to `char*` [first element of an array ] – Cholthi Paul Ttiopic Sep 23 '15 at 06:32
  • @CholthiPaulTtiopic - No. First of all, that should be "we could say it's a pointer to `char`" (not `char*`). BUT, you may use it as a pointer to `char` in some cases, but that doesn't make it one. "5.0" may be an integer, but it's not an `int`, although you may use it in some places that take an `int`. – James Curran Sep 23 '15 at 22:01
25

Yes, your compiler is expecting void *. Just cast them to void *.

/* for instance... */
printf("The value of s is: %p\n", (void *) s);
printf("The direction of s is: %p\n", (void *) &s);
indiv
  • 17,306
  • 6
  • 61
  • 82
  • 1
    That is wrong. Casting to void* is merely hiding the problem, not solving it. – James Curran Oct 13 '08 at 14:46
  • 1
    My first thought was that he just wanted to print numbers to see the values that the compiler had assigned, and casting to void would let him, regardless of any problems in his declarations. But yes, that is certainly avoiding the issue. – indiv Oct 13 '08 at 15:19
  • 11
    Casting to `void *` for arguments to the `%p` format in a variadic function is actually __required__ by the language standard. – Jens Apr 17 '11 at 10:54
  • 2
    This is one of the rare cases where casting in C is really necessary. – alk Aug 12 '13 at 15:12
5

If you pass the name of an array as an argument to a function, it is treated as if you had passed the address of the array. So &s and s are identical arguments. See K&R 5.3. &s[0] is the same as &s, since it takes the address of the first element of the array, which is the same as taking the address of the array itself.

For all the others, although all pointers are essentially memory locations they are still typed, and the compiler will warn about assigning one type of pointer to another.

  • void* p; says p is a memory address, but I don't know what's in the memory
  • char* s; says s is a memory address, and the first byte contains a character
  • char** ps; says ps is a memory address, and the four bytes there (for a 32-bit system) contain a pointer of type char*.

cf http://www.oberon2005.ru/paper/kr_c.pdf (e-book version of K&R)

Airsource Ltd
  • 32,379
  • 13
  • 71
  • 75
3

It's not a pointer to character char* but a pointer to array of 4 characters: char* [4]. With g++ it doesn't compile:

main.cpp: In function ‘int main(int, char**)’: main.cpp:126: error: cannot convert ‘char (*)[4]’ to ‘char**’ in initialization

Moreover, the linux man pages says:

p

The void * pointer argument is printed in hexadecimal (as if by %#x or %#lx). It shoud be pointer to void.

You can change your code to:

char* s = "asd";
char** p = &s;

printf("The value of s is: %p\n", s);
printf("The address of s is: %p\n", &s);

printf("The value of p is: %p\n", p);
printf("The address of p is: %p\n", &p);

printf("The address of s[0] is: %p\n", &s[0]);
printf("The address of s[1] is: %p\n", &s[1]);
printf("The address of s[2] is: %p\n", &s[2]);

result:

The value of s is: 0x403f00

The address of s is: 0x7fff2df9d588

The value of p is: 0x7fff2df9d588

The address of p is: 0x7fff2df9d580

The address of s[0] is: 0x403f00

The address of s[1] is: 0x403f01

The address of s[2] is: 0x403f02

4pie0
  • 29,204
  • 9
  • 82
  • 118
  • Hi, thanks for answering this old question. Luckily I have learn a lot more about programming in this past 4 years. But I think your intention is to give a better insight to the persons who arrive to this question. Considering that, I'll have to say that compiling C code with a C++ compiler is not a good thing, they are not the same language, and confusing it make take to bugs. PS: this comment box sucks haha – alcuadrado Mar 15 '13 at 18:25
  • @alcuadrado oh, didn't see the date, it is prehistoric ! – 4pie0 Mar 15 '13 at 18:26
  • @where_is_tftp can you explain what is meant with "direction" here? "The direction of s", I don't understand what it's meant with "direction" of a pointer. – zumzum Oct 03 '16 at 12:17
  • @zumzum This is OP's convention, it was meant to be "address", I have corrected this in my answer. Thank you. – 4pie0 Oct 03 '16 at 13:25
1

change line:

char s[] = "asd";

to:

char *s = "asd";

and things will get more clear

Scott
  • 4,974
  • 6
  • 35
  • 62
Sergey Golovchenko
  • 18,203
  • 15
  • 55
  • 72
  • 2
    The error will go away, but nothing will be more clear, as you fix avoids an explanation of the original problem. – James Curran Oct 13 '08 at 14:47
1

You can't change the value (i.e., address of) a static array. In technical terms, the lvalue of an array is the address of its first element. Hence s == &s. It's just a quirk of the language.

Chris Conway
  • 55,321
  • 43
  • 129
  • 155
1

Normally, it's considered poor style to unnecessarily cast pointers to (void*). Here, however, you need the casts to (void*) on the printf arguments because printf is variadic. The prototype doesn't tell the compiler what type to convert the pointers to at the call site.

fizzer
  • 13,551
  • 9
  • 39
  • 61
0

You have used:

char s[] = "asd";

Here s actually points to the bytes "asd". The address of s, would also point to this location.

If you used:

char *s = "asd";

the value of s and &s would be different, as s would actually be a pointer to the bytes "asd".

You used:

char s[] = "asd";
char **p = &s;

Here s points to the bytes "asd". p is a pointer to a pointer to characters, and has been set to a the address of characters. In other words you have too many indirections in p. If you used char *s = "asd", you could use this additional indirection.

selwyn
  • 1,184
  • 2
  • 10
  • 20