Topic: What is the value of NULL anyway?
Author: David R Tribble <david@tribble.com>
Date: Mon, 14 Jan 2002 19:17:08 GMT Raw View
David R Tribble <david@tribble.com> writes:
>> Even if the implementation has different representations for
>> different pointer types, the 'void*' type must, by the rules of the
>> language, be capable of holding a pointer value of any type
>> (specifically, that converting a data pointer to 'void*' and then
>> back to its original type results in no loss of information).
>> Which, to me, implies that passing a 'void*' value as a varargs
>> argument is always safe, even if it is not the exact pointer type
>> expected by the called function.
Richard Kettlewell wrote:
> No, for the same reason that passing an int when a long is expected
> (by a variadic function) is not safe: the representation may differ
> even if the set of values is entirely contained.
Tribble:
>> The va_arg() in the called function must do the appropriate
>> conversion to retrieve the passed value off of the calling stack
>> anyway.
Kettlewell:
> I can't see any such requirement.
Douglas A. Gwyn wrote:
> No! The called function expects the argument to already have the
> right type, and does not have any information about what type might
> have been passed instead. Because the representations can differ
> (except for char * and void *, which are required to have the same
> representation), a pointer argument of the wrong type can look like
> garbage to the called function.
Oops, you're both right.
On the other hand, all structure pointers smell alike, as do all
function pointers. Which means that only pointers to primitive
data types may have different representations (except for 'char*' and
'void*', which also smell alike).
It would be interesting to know of any extant implementations having
differently sized data pointer types.
-- David R Tribble, mailto:david@tribble.com --
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: fred@genesis.demon.co.uk (Lawrence Kirby)
Date: Wed, 16 Jan 2002 15:49:26 GMT Raw View
On Monday, in article <3C3FAAEB.8C2E6FB@tribble.com>
david@tribble.com "David R Tribble" wrote:
...
>Oops, you're both right.
>
>On the other hand, all structure pointers smell alike,
It is true that all pointers to structures must have the same
representation.
>as do all function pointers.
However that is not true for function pointers. You are guaranteed
that converting a function pointer to another type of function pointer
and back again restores the original value. However the conversions
can change the representations. Unlikely perhaps, but possible unless
you can prove otherwise.
> Which means that only pointers to primitive
>data types may have different representations (except for 'char*' and
>'void*', which also smell alike).
You have to consider pointers to unions and arrays as well as pointers
to pointers, pointers to integer and floating point types.
>It would be interesting to know of any extant implementations having
>differently sized data pointer types.
I think the most likely case is where word-addressed architectures
have extra offset information in the pointer representation when
simulating subword objects such as smaller bytes.
--
-----------------------------------------
Lawrence Kirby | fred@genesis.demon.co.uk
Wilts, England | 70734.126@compuserve.com
-----------------------------------------
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: Barry Margolin <barmar@genuity.net>
Date: Fri, 11 Jan 2002 12:20:35 CST Raw View
In article <3C3E1575.3B1EDCA3@tribble.com>,
David R Tribble <david@tribble.com> wrote:
>Barry Margolin wrote:
>> Even if the language had a special NULL keyword, I don't think it
>> would be sufficient for use in calling varargs functions. If an
>> implementation has different representations for different pointer
>> types, the caller would have no way of knowing which type of null
>> pointer to pass unless it's cast appropriately. I suppose that along
>> with the special keyword we could say that pointers are always passed
>> to varargs functions as void*, and va_arg() must coerce it to the
>> destination type, but that seems against the spirit of C -- it forces
>> two hidden type conversions to be performed. But maybe this isn't so
>> bad, since most architectures don't have different pointer
>> representations, so the conversions usually don't actually have to do
>> anything.
>
>Even if the implementation has different representations for different
>pointer types, the 'void*' type must, by the rules of the language, be
>capable of holding a pointer value of any type (specifically, that
>converting a data pointer to 'void*' and then back to its original type
>results in no loss of information).
But you have to request that conversion. This takes place automatically in
assignments (since the types of the two operands are known) and calls to
prototyped functions (since the type of the argument is known). But when
you call a varargs function, pointer values are passed with no conversions,
right?
> Which, to me, implies that passing
>a 'void*' value as a varargs argument is always safe, even if it is not
>the exact pointer type expected by the called function. The va_arg()
>in the called function must do the appropriate conversion to retrieve
>the passed value off of the calling stack anyway.
Only if we were to specify that pointer values are always converted to
void* when calling varargs functions. Otherwise, va_arg() will try to
invert a conversion that was never done in the first place.
--
Barry Margolin, barmar@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: gravrock@cheetah.it.wsu.edu (Steve Gravrock)
Date: Fri, 11 Jan 2002 12:27:48 CST Raw View
In article <3C3E1575.3B1EDCA3@tribble.com>, David R Tribble wrote:
[Passing null pointers to variadic functions]
>Even if the implementation has different representations for different
>pointer types, the 'void*' type must, by the rules of the language, be
>capable of holding a pointer value of any type (specifically, that
>converting a data pointer to 'void*' and then back to its original type
>results in no loss of information). Which, to me, implies that passing
>a 'void*' value as a varargs argument is always safe, even if it is not
>the exact pointer type expected by the called function. The va_arg()
>in the called function must do the appropriate conversion to retrieve
>the passed value off of the calling stack anyway.
We're talking about something like the following contrived example,
right?
void vfn(int arg1, ...)
{
va_list ap;
int *ip;
va_start(ap, arg1);
while ((ip = va_arg(ap, int *)) != NULL)
do_something_with(ip);
va_end(ap);
}
Now, consider these two calls to vfn:
void foo(void)
{
int *p1, *p2;
/* ... */
vfn(0, p1, p2, NULL);
vfn(0, p1, p2, (int *)NULL);
}
Assume that NULL is defined as (void *)0, and that the run-time
representation of an integer null pointer differs from that of a
generic (void *) null pointer. [ I'm reading this from comp.lang.c,
but unless I'm mistaken, a conforming implementation of either C
or C++ could have such rules. ]
In the first call to vfn, the type of the last actual argument is
void *. In the second call, the type of the last argument is int *.
In general, there is no way the compiler can know what type the
argument should be converted to. Given the implementation described
above, there are only two ways that vfn can receive a valid integer
null pointer as a result of both calls:
1. When generating code for foo, the compiler somehow "knows" what
type the argument should be converted to.
2. At run-time, va_arg somehow determines the type of the argument
it was passed and performs the appropriate conversion.
To put it more succinctly, the rules about being able to convert a
void * to any other data pointer do not apply because no conversion
is performed.
>All of which implies that a NULL keyword would work just fine for
>varargs pointer arguments.
A NULL (or null, or whatever) keyword would work as well (IOW not very)
as (void *)0. The advange of a keyword is that passing it to a
variadic function without a cast could be a constraint violation, as
others have said elsethread. Of course, all this is moot if a
programmer chooses to write 0, 0L, or (void *)0 directly instead
of using the keyword.
>On the other hand, unless your architecture is fairly forgiving,
>passing an integer 0 as a varargs argument that is expected to be
>a pointer type is a dangerous proposition. (Of course, most modern
>CPU architectures are indeed that forgiving.)
Right.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: Steve Clamage <clamage@eng.sun.com>
Date: Fri, 11 Jan 2002 15:10:49 CST Raw View
On Thu, 10 Jan 2002, Paul Smith wrote:
>
> %% David R Tribble <david@tribble.com> writes:
>
> drt> Even if the implementation has different representations for
> drt> different pointer types, the 'void*' type must, by the rules of
> drt> the language, be capable of holding a pointer value of any type
> drt> (specifically, that converting a data pointer to 'void*' and then
> ^^^^^^^^^^^^
> drt> back to its original type results in no loss of information).
>
> Right. What about function pointers though?
There is no requirement that void* be able to hold a function pointer,
or that casting between void* and a function pointer (in either
direction) yields a usable result. In C++, such a cast requires
a diagnostic message, but in C no diagnostic is required.
--
Steve Clamage, stephen.clamage@sun.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: Sat, 12 Jan 2002 09:52:23 CST Raw View
"Michel de Becdeli vre" wrote:
...
> In C NULL is #defined as 0 or (void*) 0 and it is up to the compiler to
C isn't that specific about NULL; there's lots of other ways to define
it. However, I agree with you that those two ways are sufficient to
demonstrate the problem.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: David R Tribble <david@tribble.com>
Date: Sat, 12 Jan 2002 10:01:29 CST Raw View
David R Tribble <david@tribble.com> writes:
>> Even if the implementation has different representations for
>> different pointer types, the 'void*' type must, by the rules of
>> the language, be capable of holding a pointer value of any type
>> (specifically, that converting a data pointer to 'void*' and then
>> ^^^^^^^^^^^^
>> back to its original type results in no loss of information).
Paul Smith wrote:
> Right. What about function pointers though?
Yes, that is a problem. Presumably any reasonable implementation of
a 'null' keyword would take this into account, or at least require a
warning if an unadorned 'null' was being passed as a varargs argument
so that such things could be avoided by the programmer.
I have expressed the desire in the past for a standard-blessed
"generic" function pointer type, akin to the generic data pointer type
'void*', for passing pointers to functions around without loss of
imformation. A possible candidate is 'void (*)(void)' or perhaps even
'void (*)()'.
The next reasonable thing to ask for would be a generic null function
pointer of this type, perhaps as a macro named 'NULLFUNC' or the like.
The alternative is to mandate that function pointers can be converted
to and from 'void*' without loss of information, but I don't think
that's reasonable, desirable, or even feasible.
-- David R Tribble, mailto:david@tribble.com --
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: fred@genesis.demon.co.uk (Lawrence Kirby)
Date: Mon, 14 Jan 2002 15:54:58 GMT Raw View
On Thursday, in article <3C3E1575.3B1EDCA3@tribble.com>
david@tribble.com "David R Tribble" wrote:
...
>Even if the implementation has different representations for different
>pointer types, the 'void*' type must, by the rules of the language, be
>capable of holding a pointer value of any type (specifically, that
>converting a data pointer to 'void*' and then back to its original type
>results in no loss of information).
A void * object cannot hold a value of any type other than void *.
You can *convert* from a pointer to object or incomplete type to void *
and back again and get the original value back. But it is important
to realise that the conversion steps in both directions are essential here.
Conversions can produce a change of representation.
> Which, to me, implies that passing a 'void*' value as a varargs argument
>is always safe, even if it is not the exact pointer type expected by the
>called function.
No, because that does not create the necessary conversion step from
void * back to the original point type. More directly the standard says
"If there is no actual next argument, or if type is not compatible with
the type of the actual next argument (as promoted according to the
default argument promotions), the behavior is undefined, except for the
following cases:
- one type is a signed integer type, the other type is the corresponding
unsigned integer type, and the value is representable in both types;
- one type is pointer to void and the other is a pointer to a character
type."
So passing void * where the function calls va_arg with, say, int *
has undefined behaviour.
>The va_arg() in the called function must do the appropriate conversion
>to retrieve the passed value off of the calling stack anyway.
The standard disagrees with you.
--
-----------------------------------------
Lawrence Kirby | fred@genesis.demon.co.uk
Wilts, England | 70734.126@compuserve.com
-----------------------------------------
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: David R Tribble <david@tribble.com>
Date: Thu, 10 Jan 2002 16:36:09 CST Raw View
David R Tribble wrote:
>> But NULL is guaranteed to work, whatever its value, when it is passed
>> as a pointer to a varargs function (even though it might not have the
>> exact pointer type required). It's up to the implementation to
>> define NULL so that this is indeed the case.
>>
>> (and)
>>
>> Most systems can get away with a NULL defined as 0 or 0L, but the
>> most portable definition is ((void)*0). (Even though the definition
>> of NULL is not required to be portable.)
Barry Margolin wrote:
> Even if the language had a special NULL keyword, I don't think it
> would be sufficient for use in calling varargs functions. If an
> implementation has different representations for different pointer
> types, the caller would have no way of knowing which type of null
> pointer to pass unless it's cast appropriately. I suppose that along
> with the special keyword we could say that pointers are always passed
> to varargs functions as void*, and va_arg() must coerce it to the
> destination type, but that seems against the spirit of C -- it forces
> two hidden type conversions to be performed. But maybe this isn't so
> bad, since most architectures don't have different pointer
> representations, so the conversions usually don't actually have to do
> anything.
Even if the implementation has different representations for different
pointer types, the 'void*' type must, by the rules of the language, be
capable of holding a pointer value of any type (specifically, that
converting a data pointer to 'void*' and then back to its original type
results in no loss of information). Which, to me, implies that passing
a 'void*' value as a varargs argument is always safe, even if it is not
the exact pointer type expected by the called function. The va_arg()
in the called function must do the appropriate conversion to retrieve
the passed value off of the calling stack anyway.
All of which implies that a NULL keyword would work just fine for
varargs pointer arguments.
On the other hand, unless your architecture is fairly forgiving,
passing an integer 0 as a varargs argument that is expected to be
a pointer type is a dangerous proposition. (Of course, most modern
CPU architectures are indeed that forgiving.)
-- David R Tribble, mailto:david@tribble.com --
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Smith"<pausmith@nortelnetworks.com>
Date: Thu, 10 Jan 2002 17:29:51 CST Raw View
%% David R Tribble <david@tribble.com> writes:
drt> Even if the implementation has different representations for
drt> different pointer types, the 'void*' type must, by the rules of
drt> the language, be capable of holding a pointer value of any type
drt> (specifically, that converting a data pointer to 'void*' and then
^^^^^^^^^^^^
drt> back to its original type results in no loss of information).
Right. What about function pointers though?
--
-------------------------------------------------------------------------------
Paul D. Smith <pausmith@nortelnetworks.com> HASMAT--HA Software Mthds & Tools
"Please remain calm...I may be mad, but I am a professional." --Mad Scientist
-------------------------------------------------------------------------------
These are my opinions---Nortel Networks takes no responsibility for them.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: Ron Natalie <ron@sensor.com>
Date: Thu, 10 Jan 2002 17:50:20 CST Raw View
Paul Smith wrote:
..
>
> Right. What about function pointers though?
>
No such guarantee.
You can however go from function pointer to some other function
pointer (not necessarily the same type) and back with no loss
of information.
Essentially you can use: void (*)() as you generic function
pointer type.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Douglas A. Gwyn" <DAGwyn@null.net>
Date: Fri, 11 Jan 2002 10:07:24 CST Raw View
David R Tribble wrote:
> Even if the implementation has different representations for different
> pointer types, the 'void*' type must, by the rules of the language, be
> capable of holding a pointer value of any type (specifically, that
> converting a data pointer to 'void*' and then back to its original type
> results in no loss of information). Which, to me, implies that passing
> a 'void*' value as a varargs argument is always safe, even if it is not
> the exact pointer type expected by the called function. The va_arg()
> in the called function must do the appropriate conversion to retrieve
> the passed value off of the calling stack anyway.
No! The called function expects the argument to already have the
right type, and does not have any information about what type might
have been passed instead. Because the representations can differ
(except for char * and void *, which are required to have the same
representation), a pointer argument of the wrong type can look like
garbage to the called function.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Michel de Becdeli vre" <BCV@inlog.fr>
Date: Fri, 11 Jan 2002 11:11:20 CST Raw View
> All of which implies that a NULL keyword would work just fine for
> varargs pointer arguments.
>
> On the other hand, unless your architecture is fairly forgiving,
> passing an integer 0 as a varargs argument that is expected to be
> a pointer type is a dangerous proposition. (Of course, most modern
> CPU architectures are indeed that forgiving.)
>
> -- David R Tribble, mailto:david@tribble.com --
In C NULL is #defined as 0 or (void*) 0 and it is up to the compiler to
translate it to whatever works on the architecture, not always 0. If you
have an union of a pointer and an integer long enough to avoid truncation on
your architecture :
pseudo code :
union { void *p; long l; } u;
doing :
u.p = NULL;
gives no garanty to have l == 0, even if sizeof( u.l ) = sizeof( u.p ),
the case was probably fairly rare at the time this was decided, and is
probably non existent today, but this is, I believe, the reason why NULL ==
0 syntactically.
Wonder how if this as been kept in standard C++.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: Fri, 11 Jan 2002 11:56:15 CST Raw View
David R Tribble wrote:
...
> Even if the implementation has different representations for different
> pointer types, the 'void*' type must, by the rules of the language, be
> capable of holding a pointer value of any type (specifically, that
> converting a data pointer to 'void*' and then back to its original type
> results in no loss of information). ...
True. It must be able to hold that pointer value, AFTER a conversion to
'void*'. It isn't necessarily capable of holding a value that has not
been so converted. Section 6.2.3.2p2 in the C standard, 4.10p2 in the
C++ standard (since this is crossposted to newsgroups for both
languages). In neither language does that conversion occur automatically
in a varargs context.
> ... Which, to me, implies that passing
> a 'void*' value as a varargs argument is always safe, ...
Also true, so long it is indeed a 'void*' value you are passing, and if
you use va_arg(ap,void *) to retrieve it. Per 7.15.1.1p2 of the C
standard, it's also legal to have either the argument type or the
va_arg() type be a pointer to a character type, if the other type is
'void*'. However, there is a strict definition of the default argument
promotions in section 6.5.2.2p6 of the C standard, and in section
5.2.2p7 of the C++ standard. In neither case does the process include an
automatic conversion of arbitrary pointer types to 'void*'. If there
were, that would mean you'd have to always retrieve the pointer with
'void*', and then cast it to the correct type. In reality, you must
retrieve it with the pointer's actual type, and no further casting is
required.
> ... even if it is not
> the exact pointer type expected by the called function. The va_arg()
> in the called function must do the appropriate conversion to retrieve
> the passed value off of the calling stack anyway.
There's a general requirement that the promoted type of an argument must
match the type used to extract it with va_arg(). This is 7.15.1.1p2 in
the C standard, and incorporated by reference into the C++ standard per
17.4.1.1p4 . What does either standard say that has the effect of
setting aside that requirement for NULL?
> All of which implies that a NULL keyword would work just fine for
> varargs pointer arguments.
In C, there's no way easy way for a program to determine the exact type
of NULL. In C++, there are ways, using templates. However, without such
type deduction, there's no way to gurantee that you can extract it
without violating 7.15.1.1p2.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]