1

I was wondering how void pointers are implemented. I tried to find it out on Godbolt with x86-64 (you can see it here), but it didn't reveal anything. How are void pointers implemented?

Edit: This is the code I used:

int main() {
    int volatile y = 123;
    void* volatile x = &y;
}

All I was trying to see here is what x would look like. I put volatile so that gcc wouldn't eliminate it as dead code.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
xilpex
  • 3,097
  • 2
  • 14
  • 45
  • I'm not sure what you're expecting to see there. `y` is at -12(%rsp) and `x` is at -8(%rsp). It loads to pointer to `y` into rax, stores the value 123 into -12(%rsp), then stores rax into -8(%rsp). – David Wohlferd Jun 28 '20 at 19:45
  • I'd suggest including your experiment code in the question, along with some kind of reason for designing that experiment that way. – Peter Cordes Jun 28 '20 at 19:59
  • @PeterCordes -- I updated my question. – xilpex Jun 28 '20 at 20:07

3 Answers3

5

Generally speaking, all pointers on an x86-64 processor are simply 8 byte values containing some memory address. Only the compiler cares about what they point it.

dbush
  • 205,898
  • 23
  • 218
  • 273
3

From my perspective, the asm clearly reveals that void* uses the same object-representation as other pointers, such as int*, so casting to and from void* is a no-op that just keeps the compiler's type system happy.

Everything in asm is just bytes that you can do integer operations on if you want. Pointers are just integers that you can dereference. e.g. in x86-64 asm, +1 to a uintptr_t is no different than +1 to a char*, because C defines sizeof(char) as 1, and x86-64 is byte addressable so every integer increment to a pointer is a new byte. (So C implementations on x86-64 use CHAR_BIT=8).

void* is just a type you that can hold any pointer value, but that doesn't let you do math on it with +1 or whatever. OTOH in C you don't need to cast to assign other pointer types to/from it.


All of this follows from x86-64 having a flat memory model, not seg:off or something. And that function pointers have the same representation as data pointers. On hypothetical machines (or C implementations) you might see something special for void*.

e.g. a machine that emulated CHAR_BIT=8 on top of word-addressable memory might have sizeof(char*) > sizeof(int*), but void* would have to be wide enough to hold any possible pointer and might even have a different format than either. (Having a narrow char that you can't actually store in a thread-safe way (without a non-atomic RMW of the containing word) wouldn't be viable for C11.)

Semi-related: Does C have an equivalent of std::less from C++? talks about how C pointers might work in other hypothetical non-simple implementations. A seg:off pointer model wouldn't make void* different from int* though.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
3

Adding to the answer above, memory has no type, whatsoever. Any generally speaking, every pointer type is basically an unsigned long integer value, pointing to some address in the memory.

Originally C language didn't have any void*, char* was the generic pointer type. And to quote from C: A Reference Manual:

the problem with this use of char* is that the compiler cannot check that programmers always convert the pointer type properly

Waqar
  • 8,558
  • 4
  • 35
  • 43