1

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
zwol
  • 135,547
  • 38
  • 252
  • 361
iBug
  • 35,554
  • 7
  • 89
  • 134
  • Probably compiler dependent, gcc doesn't mind – Déjà vu Nov 23 '17 at 01:56
  • @ringø What's the standard's specification? – iBug Nov 23 '17 at 01:57
  • 3
    I don't think the other question is saying that `(void *)(void *)` is an invalid conversion in general. What he's saying is that it doesn't meet the criteria to be a **null pointer constant**. – Barmar Nov 23 '17 at 02:11
  • 1
    Converting a pointer to object (`void *`) into a pointer to function (`void (*)(void *)`) is not guaranteed to be reversible , nor vice versa. – Jonathan Leffler Nov 23 '17 at 02:12
  • @ringø With `-Wpedantic` you should get an error message along the lines of " warning: ISO C forbids initialization between function pointer and ‘void *’". – zwol Nov 23 '17 at 02:55
  • in his answer, the conversion he's talking about is the one that is implicit in the assignment, not the sequence of casts used to produce the operand. – lockcmpxchg8b Nov 23 '17 at 03:01
  • 1
    Are you sure that you used 'c' compiler, not 'c++'. There should be no problem with 'c'. There would be a problem with 'c++'. The latter would not allow you to assigh 'void*' to the function pointer. – Serge Nov 23 '17 at 03:03
  • @Serge The original answer states it's *Invalid in both C and C++*. – iBug Nov 23 '17 at 03:06
  • I just wanted to make sure. In this case you have a faulty 'c' compiler. – Serge Nov 23 '17 at 03:10
  • 3
    The part of the code `(void *)(void *)0` is valid. The problem is that it is not a valid initializer for a function pointer. – M.M Nov 23 '17 at 03:27
  • Is it a C or C++ question? – curiousguy Nov 24 '17 at 06:59
  • @curiousguy Both, as the original answer is. – iBug Nov 24 '17 at 07:00

1 Answers1

4

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.

zwol
  • 135,547
  • 38
  • 252
  • 361