18

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?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
de1337ed
  • 3,113
  • 12
  • 37
  • 55

2 Answers2

24

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.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • 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. – de1337ed Apr 07 '12 at 20:50
  • 1
    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 ... – Daniel Fischer Apr 07 '12 at 20:53
7

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 *)
Community
  • 1
  • 1
Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
  • I don't understand why are you implying that we can't cast from or to `char*`? – Mehdi Charife Aug 18 '23 at 18:12
  • @MehdiCharife -- how to you get that from this answer? It says _"`(void *)` and `(char *)` can be both equivalently cast to any other pointer type...."_ – ad absurdum Aug 19 '23 at 19:15
  • @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*)` – Mehdi Charife Aug 19 '23 at 19:18
  • 2
    @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. – ad absurdum Aug 19 '23 at 19:19
  • 1
    @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. – ad absurdum Aug 19 '23 at 19:35
  • @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*`. – Mehdi Charife Aug 19 '23 at 19:38
  • 1
    @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. – ad absurdum Aug 19 '23 at 19:43