Null constant pointer as argument of no-prototype call

L

Luca Forlizzi

Hello,

today I noticed that passing a null constant pointer as argument to a
function
called with no prototype visible, in corrispondence of a pointer
parameter, is undef.b.
This is because when no prototype is visible the argument should be
compatible with
the parameter, and the "null constant pointer" is really an integer
expression.
Is that right?

If so, isn't there a difference between the case where the null
constant pointer
is defined as 0 and the one where it is defined as (void *)0 ? It
seems to me that
if the parameter has type void *, in the formed case there is
unde.b., while in the
latter the behavior is defined.

In case what I wrote above is true, are there real implementations
where it
does not work ?
(I imagine that in implementations pre-prototypes, passing a
null constant pointer does work)

Luca Forlizzi
 
K

Keith Thompson

Luca Forlizzi said:
today I noticed that passing a null constant pointer as argument to a
function called with no prototype visible, in corrispondence of a
pointer parameter, is undef.b. This is because when no prototype is
visible the argument should be compatible with the parameter, and the
"null constant pointer" is really an integer expression. Is that
right?

"undef.b" means "undefined behavior, right?

And it's "null pointer constant", not "null constant pointer".

A null pointer constant is either an integer constant expression
with the value 0, or such an expression cast to void*. Its type
can be any integer type or void*, but it's most commonly either
int or void*.

If you're calling a function with no visible prototype, the promoted
type of the argument must match the type of the parameter. Which means,
for example, that free(NULL) can have undefined behavior if NULL is
defined as 0.

The solution is simple: *always* have a visible prototype for any
function you call.

This doesn't help for arguments corresponding to the "..." for
variadic functions; for example, printf("%p\n", NULL) has the same
problem, even if the prototype is visible. The solution there is to
cast the argument to the desired type: printf("%p\n", (void*)NULL);
If so, isn't there a difference between the case where the null
constant pointer is defined as 0 and the one where it is defined as
(void *)0 ? It seems to me that if the parameter has type void *, in
the formed case there is unde.b., while in the latter the behavior is
defined.
Yes.

In case what I wrote above is true, are there real implementations
where it does not work ? (I imagine that in implementations
pre-prototypes, passing a null constant pointer does work)

The phrase "does not work" is tricky in the presence of undefined
behavior. There is no correct behavior, so strictly speaking it
can neither "work", nor "not work".

Certainly. If you use 0, which is a null pointer constant, in a
pointer context (for example, if the implementation has "#define
NULL 0"), and int and void* are of different sizes, it's likely
to fail in some arbitrarily bad way, such as passing unexpected
values or crashing your program. Likewise if integer and pointer
arguments are passed differently, say in different registers.
And yes, the same program can appear to "work", or can blow up,
depending on how the implementation chooses to define NULL.

So don't do that.
 
L

Luca Forlizzi

Luca Forlizzi <[email protected]> writes:
"undef.b" means "undefined behavior, right?
yes


And it's "null pointer constant", not "null constant pointer".

ops... I apologise
If you're calling a function with no visible prototype, the promoted
type of the argument must match the type of the parameter.  Which means,
for example, that free(NULL) can have undefined behavior if NULL is
defined as 0.

The solution is simple: *always* have a visible prototype for any
function you call.

Agreed, I was not seeing the issue as a problem, and I always use
prototypes, I was just interested in understanding what the standard
says. Thanks to your explanations, I have a better understanding, now.
This doesn't help for arguments corresponding to the "..." for
variadic functions; for example, printf("%p\n", NULL) has the same
problem, even if the prototype is visible.  The solution there is to
cast the argument to the desired type: printf("%p\n", (void*)NULL);

so, this means that one can not use %p to print the value of
a pointer to a function, right?

The phrase "does not work" is tricky in the presence of undefined
behavior.  There is no correct behavior, so strictly speaking it
can neither "work", nor "not work".

Yes I knew that, I meant to say "does not work as if a correct
prototype for the called function were visible". Sorry for being
sloppy.

And yes, the same program can appear to "work", or can blow up,
depending on how the implementation chooses to define NULL.

So don't do that.

I won't. But the information you give me, might be useful to know to
find elusive bugs. Thanks, Keith
 
K

Keith Thompson

Luca Forlizzi said:
so, this means that one can not use %p to print the value of
a pointer to a function, right?

Indirectly, yes. You can write:
printf("%p\n", (void*)func_ptr);
and it doesn't violate any constraint as far as I can tell, but
the standard doesn't define the behavior of a conversion from a
pointer-to-function type to void*.

I note that one popular compiler produces a warning when invoked
in conforming mode:

warning: ISO C forbids conversion of function pointer to object pointer type

I believe this warning is incorrect.

(The cast is likely to work as expected on many implementations.)

[...]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top