[c] Printing pointers in C

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?

This question is related to c pointers

The answer is


change line:

char s[] = "asd";

to:

char *s = "asd";

and things will get more clear


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.


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


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)


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.


change line:

char s[] = "asd";

to:

char *s = "asd";

and things will get more clear


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.


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.


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)


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.


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);

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


change line:

char s[] = "asd";

to:

char *s = "asd";

and things will get more clear


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)


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.


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);

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.


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.


change line:

char s[] = "asd";

to:

char *s = "asd";

and things will get more clear


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.


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.


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);

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)