5

I thought that in gcc, void * and char * are treated the same way when it comes to pointer arithmetic, i.e. void * "points" to a single byte in memory, so the following code

void *p;
p = malloc(sizeof(void));
printf("%p %p\n",p,p+1);

indeed returns 0x984a008 0x984a009. Similarly, void ** points to a pointer, so an increment by one really means an increment by 4 bytes (on a 32 bit OS), i.e.

void **p;
p = (void **) malloc(sizeof(void *));
printf("%p %p\n",p,p+1);

returns 0x984a008 0x984a00c. However, the following code confuses me

void **p, *p1;
p = (void **) malloc(sizeof(void *));
p1 = (void **) p;
printf("%p %p\n",p1,p1+1);

Since it returns again 0x984a008 0x984a009. What is going on here?

Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
Ivan
  • 409
  • 4
  • 17
  • 2
    Perhaps you meant to type `void **p, **p1;` instead of `void **p, *p1;` ? – Paul R Mar 05 '13 at 16:18
  • 1
    `sizeof(void)` is 1 or at least gives a warning, it's useless. You don't do that. What does a `void` type indicate?? there's only `void*`. – Tony The Lion Mar 05 '13 at 16:20
  • I did mean void **p, *p1. I know this code compiles, what I don;t understand is why if p is originally declared as void **, p+1 corresponds to an increment by 4 bytes, while if p1 is declared void * and later recast as void **, the arithmetic only adds 1 byte for p1+1 – Ivan Mar 05 '13 at 16:22
  • Why is that, is the cast to `void **` ignored? – Ivan Mar 05 '13 at 16:24
  • 1
    @Ivan: Yes, obviously it's ignored. It's an identity cast (the expression `p` already had type `void**`). Identity casts are ignored except for some effect on value category (e.g. the result of the cast is an rvalue) – Ben Voigt Mar 05 '13 at 16:37

5 Answers5

7

Ignoring the possible undefined behaviour of void pointer arithmetic for the moment...

The type of p1 is void *.

You can't change a variable's type by assigning a value of a different type to it. p1 will always stay void *.

Any expression of a different type assigned to it will implicitly be cast to void * (or give an error if it can not).

Thus it's essentially the same as the first example.

EDIT:

As far as I know, casting from one pointer type to another doesn't actually do anything, its main purpose is for type-checking.

A pointer is just a memory address, a number, so essentially the memory looks something like: (post-assignment)

  p1       p2
void *   void** <- these types are fixed and known during compilation
------   ------
|1234|   |1234|         at address 1234 = the 4 bytes from malloc
------   ------
  ^
  |
this value is the only thing that will change by assigning p1 to a different value
Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
  • 3
    On some architectures, casting from one pointer type to another does change something. On your typical x86(_64) architecture, though, you can expect all pointers to have the same representation and casts being a no-op. – Daniel Fischer Mar 05 '13 at 17:11
3

You should use char * instead of void *, since arithmetic on pointer to void is a gcc extension.

char *p1 = /* ... */;

printf("%p %p\n", p1, p1+1);

Whatever points p, the pointer arithmetic on p uses char * type (not char **).

If you write:

char *p1 = /* ... */;

printf("%p %p\n", p1, (char**)p1+1);

Pointer arithmetic uses char **.

md5
  • 23,373
  • 3
  • 44
  • 93
2

When you operate with void *, the increment is 1. When you use void **, it's the size of a pointer.

In the operation that confuses you, your void * cast to void ** is being implicitly made back into a void *. It's as if you did this:

long a, b, c;
c = a + (int) b;

You cast b to int, but then you want to operate with a long, so it's cast back.

salezica
  • 74,081
  • 25
  • 105
  • 166
  • That's fine, but why does the compiler do the recast back? Both `void *` and `void **` take 4 bytes on the stack and as far as I understand all the pointer arithmetic is done at compile time. So why does the compiler insist that p1 is cast back? – Ivan Mar 05 '13 at 16:32
  • Think of it this way: it makes sense to cast `void **` to `void *` -- any pointer can become `void *`. Casting the other way round, on the other hand, dose not. Not all pointers are double pointers. The compiler does the sensitive thing with the operation you requested: it makes sense of it the only way it can. – salezica Mar 05 '13 at 16:34
1

The void pointer can not be incremented. It is undefined behavior.

Related question: Increment void pointer by one byte? by two?

Community
  • 1
  • 1
Alex
  • 9,891
  • 11
  • 53
  • 87
  • OK, but if you change all the types to `char` pointers the question still stands (although GCC will give you a warning in that case) – Rup Mar 05 '13 at 16:27
0

I know I'm posting over a year later, but I happened upon this question and it intrigued me.

I agree with @Dukeling that you can't change a variable's type just by casting it. But it seems it depends upon what the compiler deems void to be. Take this example program, and look at the resulting output. Notice that the only difference between vp and vp2 is the sizeof() portion of the malloc().

Compiled on: gcc (Debian 4.7.2-5) 4.7.2
Compile line: gcc -o void_test void_test.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
  void *vp, *vp2;

  printf("sizeof(void)   = %d\n", sizeof(void));
  printf("sizeof(void *) = %d\n", sizeof(void *));
  printf("sizeof(char)   = %d\n", sizeof(char));
  printf("sizeof(char *) = %d\n\n", sizeof(char *));

  vp = (void *) malloc(sizeof(void));
  vp2 = (void *) malloc(sizeof(void *));

  printf("vp    = %p\n", vp);
  printf("vp+1  = %p\n", vp+1);
  printf("vp2   = %p\n", vp);
  printf("vp2+1 = %p\n", vp2+1);

  return 0;
}

Gives the following output:

$ ./void_test 
sizeof(void)   = 1
sizeof(void *) = 8
sizeof(char)   = 1
sizeof(char *) = 8

vp    = 0x1ee3010
vp+1  = 0x1ee3011
vp2   = 0x1ee3010
vp2+1 = 0x1ee3031
RoraΖ
  • 634
  • 9
  • 21