1

It is a familiar feature of C that a void* can be assigned to or from any pointer variable. In N1570, the draft standard document for C11, this is specified in 6.3.2.3 Pointers:

A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

But 6.2.7 Compatible type and composite type says

All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.

Yet, as far as I can see, that section does not say void* is compatible with other pointer types. So:

int x = 5;
int *xp = &x;
void *vp = xp;

is expected to be perfectly valid by tradition and 6.3.2.3, but would seem to be undefined behavior by 6.2.7.

What am I missing?

rwallace
  • 31,405
  • 40
  • 123
  • 242

2 Answers2

4

Keyword: All declarations ...

int x = 5;
int *xp = &x;
void *vp = xp;

These are three declarations, declaring three separate objects: x, xp, and vp.

What the section you quoted means is that if one file says

extern int foo;

and another file says

extern double *foo;

the behavior is undefined because foo has been declared twice, with different types.

melpomene
  • 84,125
  • 8
  • 85
  • 148
1

Q: Yet, as far as I can see, that section does not say void* is compatible with other pointer types /--/ ...perfectly valid by tradition and 6.3.2.3, but would seem to be undefined behavior by 6.2.7.

6.2.7 doesn't say much but points at further reading at 6.7.6.1 pointer declarations, where we can find the relevant part:

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

So no, void* isn't a compatible type with for example int*, because void isn't compatible with int. They may point at compatible types though, and you may convert freely between them.

The code you posted is fine, because it only relates to the pointed-at types. An example of undefined behavior because of non-compatible pointer types would rather be this:

void* v;
int* p = *(int**)&v;

Here the address of the pointer itself is forcibly converted to a non-compatible type. It is actually of type void** but the programmer tells the compiler to treat is as int**, then read the contents as if it was int*. Formally we read the object through a "lvalue access" of a type not compatible with the object being stored at that location.

In practice though, since void* must be converted to/from other object pointer types without losing information, it almost certainly has the same size and representation as object pointers. The C standard does not guarantee that though, so formally threating it as another type is undefined behavior.

This is related to the C concept of effective type of an object and strict aliasing.

Lundin
  • 195,001
  • 40
  • 254
  • 396