iBug
iBug

Reputation: 37227

How can converting a pointer to `void*` twice be invalid?

Regarding zwol's answer, I'm confused why

typedef void (*fp)(void);  // any pointer-to-function type will show this effect

fp c = (void *)(void *)0;  // invalid conversion in both C and C++

How is the last line invalid? To me, it sounds like converting int(0) to void* twice, where converting a pointer to the same type should be absolutely fine, like this:

int a, *pa = (int*)&a;
//           ^^^^^^ Totally OK

Upvotes: 1

Views: 160

Answers (1)

zwol
zwol

Reputation: 140495

The thing you're asking about was a contrived example, meant to illustrate a corner case of the rules of C, and the thing that's invalid isn't the double cast, it's the assignment.

If you have any pointer-to-function type, such as

typedef void (*fp)(void);

you can initialize variables of this type with any valid null pointer constant, e.g.

fp a = 0;          // canonical null pointer constant in C
fp b = (void *)0;  // another common choice
fp c = '\0';       // yes, really, this is a null pointer constant
fp d = (1-1);      // and so is this

But you can't initialize them with a pointer to any specific type other than fp itself, even if that pointer is a null pointer, and even if the specific type in question is void *.

char *x = 0;
void *y = 0;
fp e = x;     // invalid: no assignment conversion from `char *` to `fp`
fp f = y;     // invalid: no assignment conversion from `void *` to `fp`

The line from my old answer that you were confused by,

fp g = (void *)(void *)0;

is essentially the same as fp f = y above. The right-hand side of both assignments is a null pointer with type void *, but not a null pointer constant, so the assignment is invalid.

You are probably now wondering why (void *)(void *)0 isn't a null pointer constant, even though (void *)0 is a null pointer constant. This is just the way the C standard happens to be written: a null pointer constant is defined as any integer constant expression with value 0, possibly with one cast to void * in front of it. Any extra casts and it's no longer a null pointer constant (but is still a constant expression). (Integer constant expressions cannot contain internal casts to pointer types.)

In your contrasting example

int a;
int *pa = (int *)&a;

no null pointers are involved, and no integer constant expressions either, and the cast really is 100% redundant. &a could be a constant expression (if and only if a has static storage duration), but it is not an integer constant expression.

Upvotes: 4

Related Questions