Can the compiler cast `(void *) 0` in `execl(prog, arg, (void*) 0)` to a null pointer of the appropriate type?

  • A+
Category:Languages

From The Linux Programming Interface

execl(prog, arg, (char *) 0); execl(prog, arg, (char *) NULL); 

Casting NULL in the manner of the last call above is generally required, even on implementations where NULL is defined as (void *) 0.

This is because, although the C standards require that null pointers of different types should test true for comparisons on equality, they don’t require that pointers of different types have the same internal representation (although on most implementations they do).
And, in a variadic function, the compiler can’t cast (void *) 0 to a null pointer of the appropriate type.

The C standards make one exception to the rule that pointers of different types need not have the same representation: pointers of the types char * and void * are required to have the same internal representation. This means that passing (void *) 0 instead of (char *) 0 would not be a problem in the example case of execl(), but, in the general case, a cast is needed.

  1. "Casting NULL in the manner of the last call above is generally required"

    Does the C standard requires the null pointer be represented the same as (char*) 0?

  2. "in a variadic function such as execl(), the compiler can’t cast (void *) 0 to a null pointer of the appropriate type."

    Is (void *) 0 not a null pointer of a type?

    If yes, why can't the compiler cast (void *) 0 in execl(prog, arg, (void*) 0) to "a null pointer of the appropriate type"?

  3. "pointers of the types char * and void * are required to have the same internal representation. This means that passing (void *) 0 instead of (char *) 0 would not be a problem in the example case of execl()".

    Can the compiler cast (void *) 0 in execl(prog, arg, (void*) 0) to "a null pointer of the appropriate type" now?

    Why does it contradict to the quote in my point 2?

  4. If I replace (void *) 0 in execl(prog, arg, (void*) 0) with cast of 0 to any type's pointer, such as (int *) 0, can the compiler cast (int *) 0 in execl(prog, arg, (int*) 0) to "a null pointer of the appropriate type"? Thanks.

  5. For a non-variadic function call, such as in sigaction(SIGINT, &sa, (int*) 0), can the compiler cast (int *) 0 to "a null pointer of the appropriate type"?

Thanks.

 


Firstly, the compiler does not "cast" in any circumstance. A cast is a syntax construct in the source code which requests a conversion.

I will assume that when you talk about "the compiler casting" you mean to talk about implicit conversion which is the process whereby a value of one type may be converted to a value of another type, without a cast operator.

The Standard specifies precisely the contexts in which implicit conversion may be applied; there must always be a target type. For example in the code int x = Y; the expression Y can be some type that is not an int, but which has implicit conversion to int defined.

No implicit conversion is applied to function arguments that correspond to the ... part of a prototype, other than the default argument promotions. For pointer values, the default argument promotions leave them unchanged.

A common thread of your question seems to be that the compiler should somehow pretend that execl behaves as if there were a prototype in place for the last argument. But in fact there is not, and the compiler doesn't have any magic behaviour for specific functions. What you pass is what you get.


  1. The standard specifies that the value of the expression (char *)0 is a null pointer. It says nothing about the representation of null pointers, and there may be multiple different representations that are all null pointers.

  2. The execl function specification says that the argument list should be terminated by (char *)0 which is a value of type char *. A value of type void * is not a value of type char * and there are no implicit conversions in this context as discussed above.

  3. There is still no implicit conversion; the text you quote is saying that you can use the wrong type argument in this one specific situation (no prototype parameter; and char * expected but void * provided, or vice versa).

  4. That would be undefined behaviour , the text you quoted in point 3 does not apply to int *.

  5. The sigaction function has a prototype; the parameter in question is struct sigaction *oldact. When you try to initialize a prototype parameter (or any variable) with a value of different type, implicit conversion to the type of the parameter is attempted. There is implicit conversion from any null pointer value to a null pointer value of a different type. This rule is in C11 6.3.2.3/4 . So that code is OK.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: