简体   繁体   中英

C void pointer arithmetic

I thought that in gcc, void * and char * are treated the same way when it comes to pointer arithmetic, ie 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), ie

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?

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

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 ** .

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.

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM