void* vs. char* pointer arithmetic
Asked Answered
N

2

19

I'm looking through my textbook and I'm a little confused about some of the code in there. In one part, they are performing pointer arithmetic in the following way:

void* bp;
...
bp = (void*)((char*)(bp)+16);
...

But later on, they do the following:

void* bp;
...
bp = bp+16;
...

I feel like they should be two different things but they are treating it like the same thing. I feel this way because, for example, if you were to do an array access (for an integer array for example),you would do the following

int* a = malloc(n*sizeof(int));
...
q = *(a+1);
...

in this case, aren't I accessing the next 4 bytes in the integer array and not the next byte? Similarly, I feel that if I have void* a, then *(a+1) should be the next 4 bytes... Or is this not the case?

Noumenon answered 7/4, 2012 at 20:45 Comment(2)
That second example shouldn't compile.Camisado
@OliCharlesworth: It won't compile (or at least will trigger a warning) if you compile in conforming mode. gcc is not conforming by default, and implements void* arithmetic as an extension.Mackenie
Z
25

It's a slip-up. Arithmetic on void * is not defined by the standard, but some compilers offer it as an extension, behaving the same as char * for arithmetic. The second is formally not valid C, but slipped through presumably out of (bad) habit.

Zigmund answered 7/4, 2012 at 20:48 Comment(2)
So the proper way to access the next 16-bytes would be to first cast to char* and then add 16? Lol, gotta change a good amount of code now. Oh and I copied down the first one a little wrong, I made a small change but I don't think it'll make a difference to my question.Noumenon
Or you could cast to uint64_t * and add 2 ;) Yes, the portable way is to cast to a pointer to a type with known size and do arithmetic on that. If you don't need portability and your compiler documents that void * arithmetic works in a specific way, you can use that. But of course, at some point you will have to port to a different compiler ...Zigmund
Z
8

The accepted answer is a good summary and I want to make it more clear why to use one or another. ;)

Although (void *) and (char *) can be both equivalently cast to any other pointer type, it is only legal to perform pointer arithmetic on a (char *) and not with (void *) if you want to comply with Standard C.

Why both are used as pointers ? Most pointer conversions to and from (void *) can be done without a cast but the use of (char *) is a reminiscence of the old times.

GCC does not warn about pointer arithmetic on (void *) as it is not a compiler intended to be compliant with standard C but for GNU C. To behave in compliance with standard C you can use the following flags:

gcc -ansi -pedantic -Wall -Wextra -Werror <more args...>

My bottom line:

  • If you want to perform pointer arithmetic: use (char *)
  • If you want to get the pointer address in order to cast it into another type later on: use (void *)
Ziegler answered 8/10, 2018 at 9:33 Comment(7)
I don't understand why are you implying that we can't cast from or to char*?Florineflorio
@MehdiCharife -- how to you get that from this answer? It says "(void *) and (char *) can be both equivalently cast to any other pointer type...."Ute
@adabsurdum from the bottom line part. It says if you want to cast to another type you should use (void*), even though you can also cast with (char*)Florineflorio
@AntoninGAVREL -- "To behave in compliance with standard C you can use the following flags...." The -ansi flag compiles as C89, but you can also use -std= to compile under other standards, e.g., -std=c17 to compile under C17.Ute
@MehdiCharife -- The answer does not say that you can't cast to and from char *. The "bottom line" part suggests guidelines. malloc provides a good example. In ancient times (pre-C89) it returned char *: this required a cast to assign the pointer to non char pointers, e.g., int *x = (int *)malloc(sizeof *x);. But void * was added in C89 and now the idiomatic thing to do in C is int *x = malloc(sizeof *x);, i.e., don't cast when you don't need to. The answer is saying: don't use char * in cases like this, i.e., when you need a generic pointer type.Ute
@adabsurdum Some people find char* to be more suitable for serving as a generic pointer type, as it provides the benefit of allowing pointer arithmetics, which is undefined for void*.Florineflorio
@MehdiCharife -- that is exactly what the answer says: if you need to access bytes via pointer arithmetic, use char *. But char * is bad for general generic pointer usage because it requires a cast; casts add noise to code and casting can suppress warnings that you want to see. void * was introduce in C89 precisely to serve as a generic pointer type.Ute

© 2022 - 2024 — McMap. All rights reserved.