Unix Programming - system() without waiting

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > December 2006 > system() without waiting





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author system() without waiting
Gernot Frisch

2006-11-20, 7:28 am

Hi,

I want to make a system() call, but do not wait for completition.
What can I do? One solution would be to fork() and system in the child
thread, then use a sigchld handler for removing zombie processes.

Is there any std functions that do this?

--
-Gernot
int main(int argc, char** argv) {printf
("%silto%c%cf%cgl%ssic%ccom%c", "ma", 58, 'g', 64, "ba", 46, 10);}



Casper H.S. Dik

2006-11-20, 7:28 am

"Gernot Frisch" <Me@Privacy.net> writes:

>Hi,


>I want to make a system() call, but do not wait for completition.
>What can I do? One solution would be to fork() and system in the child
>thread, then use a sigchld handler for removing zombie processes.


>Is there any std functions that do this?


posix_spawn(). (Well, it's a standard function but in a very recent
standard)

Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
Mr. Uh Clem

2006-11-20, 1:18 pm

Gernot Frisch wrote:
> Hi,
>
> I want to make a system() call, but do not wait for completition.
> What can I do? One solution would be to fork() and system in the child
> thread, then use a sigchld handler for removing zombie processes.
>
> Is there any std functions that do this?
>


system("command &");


--
Clem
"If you push something hard enough, it will fall over."
- Fudd's first law of opposition
Gernot Frisch

2006-11-20, 1:18 pm


"Mr. Uh Clem" <uhclem@DutchElmSt.invalid> schrieb im Newsbeitrag
news:4561b058$0$26930$9a6e19ea@news.newshosting.com...
> Gernot Frisch wrote:
>
> system("command &");


Excellent! Will try this at home.


Hubble

2006-11-20, 1:18 pm


Gernot Frisch schrieb:

> I want to make a system() call, but do not wait for completition.
> What can I do? One solution would be to fork() and system in the child
> thread, then use a sigchld handler for removing zombie processes.


You can also "daemonize" the process by double forking. This does not
require a signal handler, since the init(8) process waits for the
subsubprocess:


pid1=fork();
if (pid1<0) {
perror("fork"); return;
}
if (pid1==0) {
/* Child */
pid2=fork();
if (pid2<0) {
perror("fork2"); _exit(2);
}
if (pid2==0) {
/* Subchild */
execlp("sh","sh","-c","cmd",NULL);
perror("exec");
_exit(1); /* init does not care */
}
_exit(0);
}
if (pid1>0) {
/*Father */
wait(&status); /* Waits for child pid1, subchild pid2
becomes child from init process */
}

(Untested)
Hubble.

Gernot Frisch

2006-11-21, 7:24 am


> pid1=fork();
> if (pid1<0) {
> perror("fork"); return;
> }
> if (pid1==0) {
> /* Child */
> pid2=fork();
> if (pid2<0) {
> perror("fork2"); _exit(2);
> }
> if (pid2==0) {
> /* Subchild */
> execlp("sh","sh","-c","cmd",NULL);
> perror("exec");
> _exit(1); /* init does not care */
> }
> _exit(0);
> }
> if (pid1>0) {
> /*Father */
> wait(&status); /* Waits for child pid1, subchild pid2
> becomes child from init process */
> }


so... noone needs to wait for pid2?


Darko

2006-11-21, 7:24 am

Nope. Init will wait for it. It's now his kid.

Gernot Frisch wrote:
>
> so... noone needs to wait for pid2?


Geoff Clare

2006-11-21, 1:19 pm

"Hubble" <reiner@huober.de> wrote, on Mon, 20 Nov 2006:

> if (pid2==0) {
> /* Subchild */
> execlp("sh","sh","-c","cmd",NULL);


This call causes undefined behaviour. (On some systems you
will be [un]lucky and it will do what you want.) To be portable
every argument must have type char *:

execlp("sh","sh","-c","cmd",(char *)NULL);

--
Geoff Clare <netnews@gclare.org.uk>

Rainer Weikusat

2006-11-21, 1:19 pm

Geoff Clare <geoff@clare.See-My-Signature.invalid> writes:
> "Hubble" <reiner@huober.de> wrote, on Mon, 20 Nov 2006:
>
>
> This call causes undefined behaviour.


It is not possible to 'cause' something 'undefined'. This is
especially absurd wrt behaviour of software, which is strictly
deterministic.

> (On some systems you will be [un]lucky and it will do what you
> want.) To be portable every argument must have type char *:
>
> execlp("sh","sh","-c","cmd",(char *)NULL);


This is not exactly true, either. Assuming that execlp behaves like a
variadic function using the stdarg.h-macros (which isn't explicitly
defined anywhere), the type of the argument that was passed must be
compatible to the type the routine expects, otherwise, the behaviour
of the call is not defined by the relevant standard (SUS):

[...]

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,

<URL:http://www.opengroup.org/onlinepubs...s/stdarg.h.html>

According to this standard, the last argument must be a null
pointer. A null pointer is a pointer that results from conversion of
a null pointer constant to a pointer type. If a null pointer of type
char * was expected, a null pointer of type void * could be used
instead. With an XSI-conformant implementation, any pointer type would
be compatible. NULL is a macro that expands to an implementation
defined null pointer constant, which may or may not have a pointer
type.

doina babu

2006-11-22, 1:17 pm


Gernot Frisch wrote:
> Hi,
>
> I want to make a system() call, but do not wait for completition.
>
> Is there any std functions that do this?


In Linux, daemon function.

Regards,
Doina Babu

Hubble

2006-11-23, 7:34 am


Geoff Clare schrieb:

> "Hubble" <reiner@huober.de> wrote, on Mon, 20 Nov 2006:
>
>
> This call causes undefined behaviour. (On some systems you
> will be [un]lucky and it will do what you want.) To be portable
> every argument must have type char *:


I doubt *all* of your statements. For what was NULL invented in the
first place. See K&R.

Hubble.

Hubble

2006-11-23, 7:34 am


Gernot Frisch schrieb:

>
> so... noone needs to wait for pid2?


After the child exits, the sub child will get init as a parent. Init is
a process which usually executes wait system calls in an endless loop.
So after the subchild exits, the zombie entry in the process table will
be reaped almost immmediately by the init process.

(use fixed width font for this)

Init -> wait wait wait wait wait wait wait wait wait wait
|
Parent -> fork() -> Parent -> wait(NULL) |
| |
Child -> fork() -> _exit(1) |
| |
Subchild -> exec -> ....... exit(0)

Note: Subchild becomes a Child of Init when its parent ("child")
_exits.

Hubble.

Gernot Frisch

2006-11-23, 7:34 am


> After the child exits, the sub child will get init as a parent. Init
> is
> a process which usually executes wait system calls in an endless
> loop.
> So after the subchild exits, the zombie entry in the process table
> will
> be reaped almost immmediately by the init process.
>
> (use fixed width font for this)
>
> Init -> wait wait wait wait wait wait wait wait wait wait
> |
> Parent -> fork() -> Parent -> wait(NULL) |
> | |
> Child -> fork() -> _exit(1) |
> | |
> Subchild -> exec -> ....... exit(0)
>
> Note: Subchild becomes a Child of Init when its parent ("child")
> _exits.


Thank you very much!


Jens Thoms Toerring

2006-11-23, 7:34 am

Hubble <reiner@huober.de> wrote:

> Geoff Clare schrieb:


[vbcol=seagreen]
> I doubt *all* of your statements. For what was NULL invented in the
> first place. See K&R.


According to the C standard a null pointer constant has to be "[a]n
integral expression with the value 0, or such an expression cast to
type void *". This allows for NULL to be defined as

#define NULL 0 /* or variations of that, e.g. 0UL */

or

#define NULL ( void * ) 0

The problem comes from cases where NULL is defined as an unadorned
0 and pointers have a different size than an int or a null pointer
has not an all-bits-zero representation. If a simple, un-cast NULL
is used as one of the unspecified argument to a variadic function
what the function actually receives on such a system is an integer
0 and not a null pointer constant (no automatic can happen since the
compiler can't determine the required type of the argument because
it's not used in pointer context). And if that isn't identical to
a null pointer constant (for one of the two possible reasons men-
tioned above) then you're in trouble. For maximum portability it's
thus necessary to cast NULL arguments in variadic functions calls
to the expected pointer types (actually, casting to any non-function
pointer type will do).

Now, POSIX may tighten the screws a bit, requiring that NULL is
defined in a way that this can't happen (e.g. as 0 cast to void *),
but that's a requirement that goes beyond the C standard. And I
don't know if this really has been made a requirement in the Single
Unix Specifications and when that happened. So I would think that
it's prudent to explicitely cast NULL to the type expected by the
variadic function - it doesn't cost you much and getting into the
habit of doing that will keep you out of trouble when you have to
program for a system where the more stringent requirement on what
NULL must be defined as doesn't exist.

Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
Geoff Clare

2006-11-23, 1:16 pm

Rainer Weikusat <rainer.weikusat@sncag.com> wrote, on Tue, 21 Nov 2006:

> Geoff Clare <geoff@clare.See-My-Signature.invalid> writes:
>
> It is not possible to 'cause' something 'undefined'. This is especially
> absurd wrt behaviour of software, which is strictly deterministic.


Okay, "results in" undefined behaviour, then. (This is the way it is
often phrased in SUSv3, although interestingly it does use "causes
undefined behavior" on the bc page under "CONSEQUENCES OF ERRORS").

>
> This is not exactly true, either. Assuming that execlp behaves like a
> variadic function using the stdarg.h-macros (which isn't explicitly
> defined anywhere), the type of the argument that was passed must be
> compatible to the type the routine expects, otherwise, the behaviour of
> the call is not defined by the relevant standard (SUS):
>
> [...]
>
> 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,
>
> <URL:http://www.opengroup.org/onlinepubs...s/stdarg.h.html>


The requirements for stdarg.h aren't relevant (more on this at the end).
In SUSv3 the execlp() function has a prototype that ends with ", ..."
and the requirements relating to how such functions can be called are
defined by the C Standard.

> According to this standard, the last argument must be a null pointer. A
> null pointer is a pointer that results from conversion of a null pointer
> constant to a pointer type.


SUSv3 says "The arguments represented by arg0,... are pointers to
null-terminated character strings. These strings shall constitute
the argument list available to the new process image. The list is
terminated by a null pointer." From the context is is fairly clear
that the null pointer must have type char *, as it terminates a list
of pointers to character strings. There is also an indication of
this requirement in the way the function is specified in the SYNOPSIS
section:

int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);

> If a null pointer of type char * was expected,
> a null pointer of type void * could be used instead.


Probably true. (C purists might argue that although the C Standard
requires char * and void * to have the same "representation and
alignment requirements", it is only in a footnote, i.e. non-normative
text, that it mentions "The same representation and alignment
requirements are meant to imply interchangeability as arguments to
functions, return values from functions, and members of unions", thus
it is not clear that implementations are required to make them
interchangeable as arguments to functions.)

> With an
> XSI-conformant implementation, any pointer type would be compatible.


Assuming you have derived this from the stdarg.h page in SUSv3, it
is not true. As I mentioned briefly above, the requirements relating
to stdarg.h have no bearing on the behaviour of functions in the
implementation that have a prototype ending ", ...". (They are not
required to be implemented in C, let alone use stdarg.h.)

--
Geoff Clare <netnews@gclare.org.uk>

Geoff Clare

2006-11-23, 1:16 pm

Reply-To: netnews@gclare.org.uk
MIME-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
Lines: 26
Organization: Zen Internet
NNTP-Posting-Host: 217.155.142.121
X-Trace: 1164294662 prichard.zen.co.uk 1492 217.155.142.121:56178
X-Complaints-To: abuse@zen.co.uk
Xref: number1.nntp.dca.giganews.com comp.unix.programmer:173230

jt@toerring.de (Jens Thoms Toerring) wrote, on Thu, 23 Nov 2006:

> According to the C standard a null pointer constant has to be "[a]n
> integral expression with the value 0, or such an expression cast to type
> void *". This allows for NULL to be defined as
>
> #define NULL 0 /* or variations of that, e.g. 0UL */
>
> or
>
> #define NULL ( void * ) 0
>

[snip]
>
> Now, POSIX may tighten the screws a bit, requiring that NULL is defined in
> a way that this can't happen (e.g. as 0 cast to void *), but that's a
> requirement that goes beyond the C standard. And I don't know if this
> really has been made a requirement in the Single Unix Specifications and
> when that happened.


POSIX/SUS does not make any additional requirements on the definition
of NULL beyond those in the C Standard.

--
Geoff Clare <netnews@gclare.org.uk>

Rainer Weikusat

2006-11-23, 1:16 pm

Geoff Clare <geoff@clare.See-My-Signature.invalid> writes:
> Rainer Weikusat <rainer.weikusat@sncag.com> wrote, on Tue, 21 Nov 2006:
>

[...]

>
> The requirements for stdarg.h aren't relevant (more on this at the end).
> In SUSv3 the execlp() function has a prototype that ends with ", ..."
> and the requirements relating to how such functions can be called are
> defined by the C Standard.


They are not, except for the variadic stdio-routines (at least, I
wasn't able to find a text relating to this. Where did you find it?).

>
> SUSv3 says "The arguments represented by arg0,... are pointers to
> null-terminated character strings. These strings shall constitute
> the argument list available to the new process image. The list is
> terminated by a null pointer." From the context is is fairly clear
> that the null pointer must have type char *, as it terminates a list
> of pointers to character strings.


No information about the type of null pointer expected is contained in the
text.

> There is also an indication of this requirement in the way the
> function is specified in the SYNOPSIS section:
>
> int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);


There is no such requirement, but required is 'a null pointer'. That
may be an omission, but until an eventual future version of the
standard specifies a type for the null pointer, that's just the way it
is.

>
> Probably true. (C purists might argue that although the C Standard
> requires char * and void * to have the same "representation and
> alignment requirements", it is only in a footnote, i.e. non-normative
> text, that it mentions "The same representation and alignment
> requirements are meant to imply interchangeability as arguments to
> functions, return values from functions, and members of unions", thus
> it is not clear that implementations are required to make them
> interchangeable as arguments to functions.)


The stdarg-text (7.15.1|2) 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.

Amusingly, this, too, relies on common sense of the reader, because no
behaviour for those two excpetional cases is defined.

>
> Assuming you have derived this from the stdarg.h page in SUSv3, it
> is not true.


It is true that the statement I made above is contained in the text.
What sort of nonsense-claim are you trying to make?
Jarosław Rafa

2006-11-24, 1:22 pm

Geoff Clare napisał(a):
> SUSv3 says "The arguments represented by arg0,... are pointers to
> null-terminated character strings. These strings shall constitute
> the argument list available to the new process image. The list is
> terminated by a null pointer." From the context is is fairly clear
> that the null pointer must have type char *, as it terminates a list
> of pointers to character strings. There is also an indication of


Hm... I was taught in my C course (several years ago...) that NULL is
the only pointer value that is compatible with *any* pointer type... In
other words, you don't need to typecast NULL (but, you may need to
typecast 0 if you prefer to write null pointer that way). Dis something
change recently?
--
Regards,
Jaroslaw Rafa
raj@ap.krakow.pl
--
Spam, wirusy, spyware... masz do¶ć? Jest alternatywa!
http://www.firefox.pl/ --- http://www.thunderbird.pl/
Szybciej. Łatwiej. Bezpieczniej. Internet tak jak lubisz.
Jens Thoms Toerring

2006-11-24, 1:22 pm

Jaros?aw Rafa <raj@ap.krakow.pl> wrote:
> Geoff Clare napisa?(a):
[vbcol=seagreen]
> Hm... I was taught in my C course (several years ago...) that NULL is
> the only pointer value that is compatible with *any* pointer type... In
> other words, you don't need to typecast NULL (but, you may need to
> typecast 0 if you prefer to write null pointer that way). Dis something
> change recently?


NULL can be defined as a simple 0 (that's allowed by the C standard).
And since system exist where it's defined that way you need to cast NULL
in places where the compiler can't determine that it's used in pointer
context. One example of this is the arguments of a variadic fucntion.

Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
Rainer Weikusat

2006-11-24, 1:22 pm

Jarosław Rafa <raj@ap.krakow.pl> writes:
> Geoff Clare napisał(a):
>
> Hm... I was taught in my C course (several years ago...) that NULL is
> the only pointer value that is compatible with *any* pointer
> type... In other words, you don't need to typecast NULL (but, you may
> need to typecast 0 if you prefer to write null pointer that way).


That was nonsense, then.

An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer
constant. If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is
guaranteed to compare unequal to a pointer to any object or
function.
(ISO C, 6.3.2.3|3)

and

The macros are

NULL

which expands to an implementation-defined null pointer
constant;
(dito, 7.17|3)

Consequently, the expansion of NULL may or may not have pointer type.
In situations where the compiler does not 'know' that a pointer
type is expected (eg arguments for a ...), a strictly conforming
ANSI-C program needs to include a cast of NULL to some pointer
type compatible with the required one.

Apart from that, a void * may be assigned to any variable that has a
pointer type (and vice versa) without casting (eventual qualifiers
must much). This is different from C++.
Hubble

2006-11-26, 7:25 am


Hubble schrieb:

> Geoff Clare schrieb:
>
>
> I doubt *all* of your statements. For what was NULL invented in the
> first place. See K&R.
>
> Hubble.


The man page of linux execle clearly says that NULL must be cast in
variadic functions like execlp (to be portable). So, Geoff war right
and I war wrong.

Hubble.

Stefaan A Eeckels

2006-11-26, 1:16 pm

On 26 Nov 2006 03:18:25 -0800
"Hubble" <reiner@huober.de> wrote:

> Hubble schrieb:
>
>
> The man page of linux execle clearly says that NULL must be cast in
> variadic functions like execlp (to be portable). So, Geoff war right
> and I war wrong.


In standard 'C', _any_ pointer can be set, or meaningfully compared, to
zero, hence 0 (or NULL) _never_ needs to be cast. That being said,
assigning a pointer, except void *, to another type needs a cast, which
is probably why the Linux manpage (hardly a reference, BTW states
that pointers should be cast in variadic functions. This does not mean
that 0 needs to be cast.

--
Stefaan A Eeckels
--
Life itself is a misery and nobody can tell what can be of it.
Those that can tell what can be of it are those who cannot tell
us because they are far from us (dead). -- Very profound scam
Casper H.S. Dik

2006-11-26, 1:16 pm

Stefaan A Eeckels <hoendech@ecc.lu> writes:

>In standard 'C', _any_ pointer can be set, or meaningfully compared, to
>zero, hence 0 (or NULL) _never_ needs to be cast. That being said,
>assigning a pointer, except void *, to another type needs a cast, which
>is probably why the Linux manpage (hardly a reference, BTW states
>that pointers should be cast in variadic functions. This does not mean
>that 0 needs to be cast.


But it does need to be cast in variadic functions; there's no
pointer context which would cause "0" (or NULL) to be automatically
promoted to a null-pointer of the appropriate type.

Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
Stefaan A Eeckels

2006-11-26, 7:18 pm

On 26 Nov 2006 19:09:54 GMT
Casper H.S. Dik <Casper.Dik@Sun.COM> wrote:

> Stefaan A Eeckels <hoendech@ecc.lu> writes:
>
>
> But it does need to be cast in variadic functions; there's no
> pointer context which would cause "0" (or NULL) to be automatically
> promoted to a null-pointer of the appropriate type.


It does not need to be of the appropriate type. Any pointer can be
compared to 0, or set to 0 - it's a feature of the 'C' language.

The only reason I can think of that would justify the need for a cast
of 0 in variadic functions is alignment (the stuff included through
stdarg.h is a complicated convoluted mess). Even that should not be a
concern as the standard mandates that default argument promotion is
applied to the non-explicitly typed function parameters, which should
ensure that 0 occupies the same number of bytes as a pointer.

--
Stefaan A Eeckels
--
You don't have to spend the rest of your life
exercising yourself to death.
-- SPAM can be so profound
Erik Max Francis

2006-11-26, 7:18 pm

Stefaan A Eeckels wrote:

> Casper H.S. Dik <Casper.Dik@Sun.COM> wrote:
>
>
> It does not need to be of the appropriate type. Any pointer can be
> compared to 0, or set to 0 - it's a feature of the 'C' language.


Only when in a pointer context. In a non-pointer context, it's just a
0. In varargs, the compiler can't possibly figure out that it will
_eventually_ end up as a pointer to type T, and thus it should make it a
null pointer of type T. So it just passes along the integral zero
value, which may or may not have the same representation as a null
pointer of the appropriate type on the given architecture. The same
occurs for cases where you're trying to pass a null pointer to a
function whose prototype is not in scope (although good programming
practice will essentially never let this happen).

The explicit cast is _always_ necessary for varargs functions, and it
doesn't matter whether you use 0 or NULL.

--
Erik Max Francis && max@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM, Y!M erikmaxfrancis
Guided by the blue light that takes you away / I'm on my way home
-- Neneh Cherry
Stefaan A Eeckels

2006-11-27, 7:22 am

On Sun, 26 Nov 2006 14:05:46 -0800
Erik Max Francis <max@alcyone.com> wrote:

> Stefaan A Eeckels wrote:
>
>
> Only when in a pointer context. In a non-pointer context, it's just
> a 0. In varargs, the compiler can't possibly figure out that it will
> _eventually_ end up as a pointer to type T, and thus it should make
> it a null pointer of type T.


An integral constant expression with value 0, or the same cast to
void*, may be converted, by a cast, by an assignment, or by a
comparison, to a pointer of any type. Notice that the conversion can be
done by assignment or comparison as well as by a cast.

> So it just passes along the integral
> zero value, which may or may not have the same representation as a
> null pointer of the appropriate type on the given architecture.


That's not correct - the compiler must perform argument promotion on
the non-explicitly typed arguments, which should ensure that alignment
issues (which could be a problem for the varargs macros) are taken care
of. And because 0 is meaningful when it comes to comparing with or
assigning to pointers, appropriate code should be generated to make
that happen in those cases where the processor's representation of the
null pointer is not the same as its representation of 0.

> The same occurs for cases where you're trying to pass a null
> pointer to a function whose prototype is not in scope (although good
> programming practice will essentially never let this happen).
>
> The explicit cast is _always_ necessary for varargs functions, and it
> doesn't matter whether you use 0 or NULL.


I looked at the latest working copy of the 'C' standard
(http://www.open-std.org/jtc1/sc22/w.../docs/n1124.pdf), which in
point 6.9 (External Definitions) states:

> 6 If the expression that denotes the called function has a type that
> does not include a prototype, the integer promotions are performed on
> each argument, and arguments that have type float are promoted to
> double. These are called the default argument promotions. If the
> number of arguments does not equal the number of parameters, the
> behavior is undefined.


> If the function is defined with a type that includes a prototype, and
> either the prototype ends with an ellipsis (, ...) or the types of
> the arguments after promotion are not compatible with the types of
> the parameters, the behavior is undefined. If the function is defined
> with a type that does not include a prototype, and the types of the
> arguments after promotion are not compatible with those of the
> parameters after promotion, the behavior is undefined, except for the
> following cases:


> — one promoted type is a signed integer type, the other promoted type
> is the corresponding unsigned integer type, and the value is
> representable in both types;


> — both types are pointers to qualified or unqualified versions of a
> character type or void.


> 7 If the expression that denotes the called function has a type that
> does include a prototype, the arguments are implicitly converted, as
> if by assignment, to the types of the corresponding parameters,
> taking the type of each parameter to be the unqualified version of
> its declared type. The ellipsis notation in a function prototype
> declarator causes argument type conversion to stop after the last
> declared parameter. The default argument promotions are performed on
> trailing arguments.


> 8 No other conversions are performed implicitly;
> in particular, the number and types of arguments are not compared
> with those of the parameters in a function definition that does not
> include a function prototype declarator.


In point 6.3.2.3 (Pointers) it states:

> 3 An integer constant expression with the value 0, or such an
> expression cast to type void *, is called a null pointer constant.
> If a null pointer constant is converted to a pointer type, the
> resulting pointer, called a null pointer, is guaranteed to compare
> unequal to a pointer to any object or function.


The question is thus whether the definition of zero as the null pointer
constant (independently of how the processor represents either 0 or the
null pointer) implies that the last sentence of clause 6 (both
are pointers...) applies, or that the first sentence of clause 8 (No
other conversions...) applies.

I only scanned through the document, but I could not find a direct
reference to the need for casting zero (NULL) to a pointer in variadic
functions. Notice that I'm not advocating against casting - it's always
better to make one's intentions clear and unambiguous. It's just that in
the specific case of zero it seems to me that the standard requires the
compiler to ensure it is treated as a pointer.

--
Stefaan A Eeckels
--
"A ship in the harbor is safe. But that's not what ships are built for."
-- Rear Admiral Dr. Grace Murray Hopper.
Rainer Weikusat

2006-11-27, 7:22 am

Erik Max Francis <max@alcyone.com> writes:

[...]

> The explicit cast is _always_ necessary for varargs functions, and it
> doesn't matter whether you use 0 or NULL.


This is not true. The cast is necessary if you use 0 and ints and
pointers are not basically the same thing (eg LP64) for your
implementation or if you use NULL and that expands to something which
does not have a type compatible with the expected pointer type (which
again depends on the implementation).
Rainer Weikusat

2006-11-27, 7:22 am

Stefaan A Eeckels <hoendech@ecc.lu> writes:
> On Sun, 26 Nov 2006 14:05:46 -0800
> Erik Max Francis <max@alcyone.com> wrote:


[...]

> An integral constant expression with value 0, or the same cast to
> void*, may be converted, by a cast, by an assignment, or by a
> comparison, to a pointer of any type. Notice that the conversion can be
> done by assignment or comparison as well as by a cast.


The compiler cannot convert the int-object 0 to a null pointer object
of a particualr type if it does not have the information that it needs
to be converted, ie if the 0 is passed as an argument for which the
relevant function prototype does not have a declaration.

>
> That's not correct - the compiler must perform argument promotion on
> the non-explicitly typed arguments, which should ensure that alignment
> issues (which could be a problem for the varargs macros) are taken care
> of.


Objects of type 'int' are not promoted in C. So, in absence of a
prototype specifiying something different, no conversion is supposed
to happen.


[...]


[...]
[vbcol=seagreen]
>
> The question is thus whether the definition of zero as the null pointer
> constant (independently of how the processor represents either 0 or the
> null pointer) implies that the last sentence of clause 6 (both
> are pointers...) applies, or that the first sentence of clause 8 (No
> other conversions...) applies.


It doesn't imply anything because the text talks about 'An integer
constant expression', which, by virtue of being an integer constant
expression with value zero, has the type 'int' (6.4.4.1|5).
Hubble

2006-11-27, 7:22 am


Stefaan A Eeckels schrieb:
> In standard 'C', _any_ pointer can be set, or meaningfully compared, to
> zero, hence 0 (or NULL) _never_ needs to be cast. That being said,
> assigning a pointer, except void *, to another type needs a cast, which
> is probably why the Linux manpage (hardly a reference, BTW


Note, that we are talking about call of the execlp *system call*. This
is OS specific, so looking at the man page for the OS is appropriate.

Hubble.

Rainer Weikusat

2006-11-27, 7:22 am

"Hubble" <reiner@huober.de> writes:
> Stefaan A Eeckels schrieb:
>
> Note, that we are talking about call of the execlp *system call*. This
> is OS specific, so looking at the man page for the OS is
> appropriate.


No, we are not talking about the 'execlp system call' because 'execlp'
is not a system call (that would be 'execve'[*]) but some kind of
variadic subroutine of the UNIX(*) standard 'C library'. As already
quoted, the argument is required to be a null pointer. In absence of
prototype information the compiler could use to convert the integer
constant '0' to a null pointer (elipsis argument to variadic routine),
it needs to be casted. 'NULL' would need to be casted if it expands to
an expression with integer type or to and expression of an
incompatible pointer type.

[*] even this isn't true: what you use as execve is again a
routine in the C library that 'somehow' incorporates a system
call.

Apart from that, the FSF does not maintain documentation in man page
format. The approriate documentation for systems using the Gnu libc
would be the glibc-manual, which is in texinfo/ info format. The man
page is just some random text, written by someone at some time, that
may or may not be correct for the library version that is actually
used.
Stefaan A Eeckels

2006-11-27, 1:18 pm

On 27 Nov 2006 01:37:05 -0800
"Hubble" <reiner@huober.de> wrote:

>
> Stefaan A Eeckels schrieb:
>
> Note, that we are talking about call of the execlp *system call*. This
> is OS specific, so looking at the man page for the OS is appropriate.


Actually, no - the language is 'C', and the OS cannot change the
standardised behaviour of the language.

--
Stefaan A Eeckels
--
Because the innovator has for enemies all those who have done well under
the old conditions, and lukewarm defenders in those who may do well
under the new. -- Niccolo Machiavelli, _The Prince_, Chapter VI
Stefaan A Eeckels

2006-11-27, 1:18 pm

On Mon, 27 Nov 2006 10:36:37 +0100
Rainer Weikusat <rainer.weikusat@sncag.com> wrote:

> Stefaan A Eeckels <hoendech@ecc.lu> writes:
>
> [...]
>
>
> The compiler cannot convert the int-object 0 to a null pointer object
> of a particualr type if it does not have the information that it needs
> to be converted, ie if the 0 is passed as an argument for which the
> relevant function prototype does not have a declaration.


It should not need to do that - the only thing it needs to ensure is
that the varargs macros can step correctly through the parameters. This
can be ensured by the appropriate sizing of the parameters. Then at run
time the appropriate action can be taken, if required, to ensure that 0
is "converted" to the null pointer.

Plus, I wasn't really considering what the compiler can and cannot do,
but what the standard says about the need to ever cast 0 to a pointer.
The status of 0 as the official null pointer constant seems to make the
cast superfluous - if not, the standard should explicitly define the
null pointer as (void *)0.

>
> It doesn't imply anything because the text talks about 'An integer
> constant expression', which, by virtue of being an integer constant
> expression with value zero, has the type 'int' (6.4.4.1|5).


It specifically says that "an integer constant expression with the
value 0, [...] is called a null pointer constant". Just as an integer
constant is an integer, a pointer constant is a pointer. Hence, 0 is
always a valid pointer and should not need to be cast. (This
equivalence seems to imply that an integer should be [at least] of the
same size as a pointer.)

Whether this is implementable, specifically in the variadic function
context (which uses macros (yuck) to implement part of the language
specification, unlike the situation in K&R 'C', where the thing was
nothing more than a hack job and recognised as such) is another
discussion.

--
Stefaan A Eeckels
--
"A ship in the harbor is safe. But that's not what ships are built for."
-- Rear Admiral Dr. Grace Murray Hopper.
Geoff Clare

2006-11-27, 1:18 pm

Rainer Weikusat <rainer.weikusat@sncag.com> wrote, on Thu, 23 Nov 2006:
> Geoff Clare <geoff@clare.See-My-Signature.invalid> writes:
> [...]
>
>
> They are not, except for the variadic stdio-routines (at least, I wasn't
> able to find a text relating to this. Where did you find it?).


Perhaps "how such functions can be called" didn't quite convey what I
meant. This is what I was thinking of:

6.5.2.2 Function calls, paragraph 7

"If the expression that denotes the called function has a type
that does include a prototype, the arguments are implicitly
converted, as if by assignment, to the types of the corresponding
parameters, taking the type of each parameter to be the
unqualified version of its declared type. The ellipsis notation in
a function prototype declarator causes argument type conversion to
stop after the last declared parameter. The default argument
promotions are performed on trailing arguments."

The "default argument promotions" are defined as part of paragraph 6.

Unless I have missed something, the descriptions of the variadic
stdio.h functions just say what argument type is expected by the
function for each conversion specifier. It is 6.5.2.2 that defines
how the arguments in the function call are converted before being
passed to the function.

>
> No information about the type of null pointer expected is contained in the
> text.


According to a strict reading, perhaps not. But it is strongly implied
by the context.

>
> There is no such requirement,


The SYNOPSIS makes the requirement. The intention of that
/*, (char *)0 */ comment is obviously to indicate that passing
(char *)0 (or the equivalent (char *)NULL) as the last argument is
the correct way to call the function, while still having the
correct prototype form for the declaration.

> but required is 'a null pointer'. That may
> be an omission, but until an eventual future version of the standard
> specifies a type for the null pointer, that's just the way it is.
>
>
> The stdarg-text (7.15.1|2) 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.
>
> Amusingly, this, too, relies on common sense of the reader, because no
> behaviour for those two excpetional cases is defined.


You are still relying on text describing <stdarg.h>, which I already
pointed out is not relevant.

>
> It is true that the statement I made above is contained in the text. What
> sort of nonsense-claim are you trying to make?


One more time: the <stdarg.h> requirements are not relevant. They
apply to variable-argument functions defined by the application, not
those provided by the implementation. Variable-argument functions
provided by the implementation do not need to be implemented in C, let
alone use the <stdarg.h> header.

--
Geoff Clare <netnews@gclare.org.uk>

Rainer Weikusat

2006-11-27, 1:18 pm

Stefaan A Eeckels <hoendech@ecc.lu> writes:
> On Mon, 27 Nov 2006 10:36:37 +0100
> Rainer Weikusat <rainer.weikusat@sncag.com> wrote:
>
> It should not need to do that - the only thing it needs to ensure is
> that the varargs macros can step correctly through the parameters.


If a va_arg call 'collides' with an argument which does not have the
correct type, the behaviour is undefined:

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
(7.15.1.1|3).

> Plus, I wasn't really considering what the compiler can and cannot do,
> but what the standard says about the need to ever cast 0 to a
> pointer.


Regarding pointer conversions, the C-standard says:

3 Conversions that involve pointers, other than where
permitted by the constraints of 6.5.16.1, shall be specified
by means of an explicit cast.
(6.5.4|3)

6.5.16.1 is the description of the assignment-operator, and contains
the following text regarding null pointer constants:

One of the following shall hold:

-- the left operand is a pointer and the right is a null
pointer constant;

This means a null pointer constant is automatically converted to a
pointer when used as the right side of an assignment whose left value
(lvalue) is a pointer, and all others uses of a null pointer constant
instead of a pointer require a cast (and function argument when a
function prototype is known are defined through assignments).

[...]

>
> It specifically says that "an integer constant expression with the
> value 0, [...] is called a null pointer constant". Just as an integer
> constant is an integer, a pointer constant is a pointer.


An integer constant with value of 0 has a type of int, as specified in
6.4.4.1|15. The next sentence after the one you quoted reads:

If a null pointer constant is converted to a pointer type, the resulting
pointer, [...]


Casper H.S. Dik

2006-11-27, 1:18 pm

Stefaan A Eeckels <hoendech@ecc.lu> writes:

>It should not need to do that - the only thing it needs to ensure is
>that the varargs macros can step correctly through the parameters. This
>can be ensured by the appropriate sizing of the parameters. Then at run
>time the appropriate action can be taken, if required, to ensure that 0
>is "converted" to the null pointer.


But the appropriate sizing and type information is not present at
the call site and so the compiler cannot properly pass "0"
(a valid null-pointer constant) in a form execlp can deal with.

Where it goes wrong is a system were the int and pointer objects do
not have the same size (your typical 64 bit Solaris system).

(The pointer passing error is easily demonstrated with sample code).

It also will fail on hypothetical systems where the null-pointer doesn't
have an all 0-bits set.

>Plus, I wasn't really considering what the compiler can and cannot do,
>but what the standard says about the need to ever cast 0 to a pointer.
>The status of 0 as the official null pointer constant seems to make the
>cast superfluous - if not, the standard should explicitly define the
>null pointer as (void *)0.


>It specifically says that "an integer constant expression with the
>value 0, [...] is called a null pointer constant". Just as an integer
>constant is an integer, a pointer constant is a pointer. Hence, 0 is
>always a valid pointer and should not need to be cast. (This
>equivalence seems to imply that an integer should be [at least] of the
>same size as a pointer.)


You're confusing the null pointer constant with a null pointer; they
are not the same thing.

Most references to a null pointer constant refer to "converting to
a null pointer of the appropriate type"

The null pointer constant does not necessarily have a pointer type:

An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant. If a null
pointer constant is //converted to a pointer type//, the resulting pointer,
called a null pointer, is guaranteed to compare unequal to a pointer to
any object or function.

Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
Rainer Weikusat

2006-11-27, 1:18 pm

Geoff Clare <geoff@clare.See-My-Signature.invalid> writes:
> Rainer Weikusat <rainer.weikusat@sncag.com> wrote, on Thu, 23 Nov 2006:

[...]
[vbcol=seagreen]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^[vbcol=seagr
een]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^[vbcol=seagreen]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^

Obnoxious emphasis added, because that appears to be something you have
missed up to now.

[...]
[vbcol=seagreen]
>
> Perhaps "how such functions can be called" didn't quite convey what I
> meant. This is what I was thinking of:


Perfectly well.

> 6.5.2.2 Function calls, paragraph 7
>
> "If the expression that denotes the called function has a type
> that does include a prototype, the arguments are implicitly
> converted, as if by assignment, to the types of the corresponding
> parameters, taking the type of each parameter to be the
> unqualified version of its declared type. The ellipsis notation in
> a function prototype declarator causes argument type conversion to
> stop after the last declared parameter. The default argument
> promotions are performed on trailing arguments."
>
> The "default argument promotions" are defined as part of paragraph
> 6.


This does define what the compiler is supposed to convert to what in
absence of a prototype. It does not define how a routine called this
way behaves wrt various argument types. C defines this explicitly for
the only set of variadic functions defined by C (see below).

> Unless I have missed something, the descriptions of the variadic
> stdio.h functions just say what argument type is expected by the
> function for each conversion specifier.


All of these are shall-constraints and the behaviour of code that
violates a shall-constraint is undefined. Because of this, I know that
the behaviour of

printf("%p", 0)

is undefined.

>
> According to a strict reading, perhaps not.


The text says 'a null pointer' without any information of the type of
null pointer. This is according to reading the words and not 'perhaps'
but verbatim.

> But it is strongly implied by the context.


'Implied meanings' of a text are interpretations of the reader.

>
> The SYNOPSIS makes the requirement.


The synopsis contains a comment, which is some kind of explanation,
but no explanation what the comment is supposed to mean.

[...]

>
> You are still relying on text describing <stdarg.h>, which I already
> pointed out is not relevant.


There is no other part of either specification that tells anything about
the behaviour of a variadic function in general. One can either assume
that the requirements for stdarg.h are supposed to be relevant (and
check this for each particular implementation in case of real
paranoia) or the question is at all unanswerable due to lack of
information.

>
> One more time: the <stdarg.h> requirements are not relevant. They
> apply to variable-argument functions defined by the application, not
> those provided by the implementation. Variable-argument functions
> provided by the implementation do not need to be implemented in C, let
> alone use the <stdarg.h> header.


Your (unbacked) statement of unconditional non-relevance would
actually forbid that they are implemented this way.
Stefaan A Eeckels

2006-11-27, 1:18 pm

On Mon, 27 Nov 2006 16:00:54 +0100
Rainer Weikusat <rainer.weikusat@sncag.com> wrote:

> Stefaan A Eeckels <hoendech@ecc.lu> writes:
>
> If a va_arg call 'collides' with an argument which does not have the
> correct type, the behaviour is undefined:
>
> 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
> (7.15.1.1|3).


The following being:

— 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.

And zero is a valid null pointer...

>
> Regarding pointer conversions, the C-standard says:
>
> 3 Conversions that involve pointers, other than where
> permitted by the constraints of 6.5.16.1, shall be specified
> by means of an explicit cast.
> (6.5.4|3)
>
> 6.5.16.1 is the description of the assignment-operator, and contains
> the following text regarding null pointer constants:
>
> One of the following shall hold:
>
> -- the left operand is a pointer and the right is a null
> pointer constant;
>
> This means a null pointer constant is automatically converted to a
> pointer when used as the right side of an assignment whose left value
> (lvalue) is a pointer, and all others uses of a null pointer constant
> instead of a pointer require a cast (and function argument when a
> function prototype is known are defined through assignments).


Again, we're inferring stuff from sections that do not directly mention
the requirement that zero be cast. Section 6.5.4|3 talks about pointer
conversions. We're not converting pointers here, we're using the null
pointer constant, which happens to be a pointer (so where's the
conversion?)

Read the text exactly as it is written:

[...] other than where permitted _by the constraints of 6.5.16.1_ [...]

meaning that you should apply _the same constraints_ as used by
6.5.15.1, not that it can only apply to assignments. In other words,
the null pointer constant does not need a cast.
>
> [...]
>
>
> An integer constant with value of 0 has a type of int, as specified in
> 6.4.4.1|15. The next sentence after the one you quoted reads:
>
> If a null pointer constant is converted to a pointer type,
> the resulting pointer, [...]


Again it's inference - the first sentence reads:

> 3 An integer constant expression with the value 0, or such an
> expression cast to type void *, is called a null pointer constant.
> If a null pointer constant is converted to a pointer type, the
> resulting pointer, called a null pointer, is guaranteed to compare
> unequal to a pointer to any object or function.


Obviously, 0 and (void *)0 are _both_ the null pointer constant, so
there never should be any reason to prefer (void *)0 to plain 0.

I'll leave it at this, in any case we're in the wrong forum for
discussions on the finer points of the 'C' language. To me it seems to
be one of these details that is not clearly spelled out in the
specification, and where creative reading and combining of sentences (or
eliding parts of them) leads to whatever one wants to hear. Discussions
like these make one appreciate how the Bible could spawn that myriad of
divergent opinions all claiming to be the truth.

Take care,

--
Stefaan A Eeckels
--
Isn't it amazing how a large number of evil morons can give the
appearance of being a single evil genius? --Mel Rimmer
Rainer Weikusat

2006-11-27, 1:18 pm

Stefaan A Eeckels <hoendech@ecc.lu> writes:

[a lot of pointless weaselwording]

> I'll leave it at this, in any case we're in the wrong forum for
> discussions on the finer points of the 'C' language. To me it seems to
> be one of these details that is not clearly spelled out in the
> specification,


It is clearly spelled out in the specification and that you refuse
accept that is solely your personal problem.

Stefaan A Eeckels

2006-11-27, 1:18 pm

On 27 Nov 2006 15:56:11 GMT
Casper H.S. Dik <Casper.Dik@Sun.COM> wrote:

> The null pointer constant does not necessarily have a pointer type:
>
> An integer constant expression with the value 0, or such an
> expression cast to type void *, is called a null pointer constant. If
> a null pointer constant is //converted to a pointer type//, the
> resulting pointer, called a null pointer, is guaranteed to compare
> unequal to a pointer to any object or function.


I think it's the standard that is confusing. Take the first sentence in
the paragraph above - it says that both 0 and (void *)0 are called the
null pointer constant, so obviously they are the same, and there should
be no reason to prefer one notation to the other. Such a reason can be
inferred from a number of more-or-less related paragraphs in the
standard (and cogitation on the obvious limitations of compilers), but
as far as I can see (from admittedly only scanning the document) it's
not unambiguously stated.

Maybe I've missed it, if not maybe the standard should be fixed.

Take care,

--
Stefaan A Eeckels
--
A business that makes nothing but money is a poor kind of business.
-- Henry Ford
Bjorn Reese

2006-11-27, 1:18 pm

Rainer Weikusat wrote:
> Erik Max Francis <max@alcyone.com> writes:
>
> [...]
>
>
>
>
> This is not true. The cast is necessary if you use 0 and ints and
> pointers are not basically the same thing (eg LP64) for your
> implementation or if you use NULL and that expands to something which
> does not have a type compatible with the expected pointer type (which
> again depends on the implementation).


You assume that pointers and ints use the same encoding, which is not
necessarily the case (hint: trap representation).

--
mail1dotstofanetdotdk
Rainer Weikusat

2006-11-27, 1:18 pm

Stefaan A Eeckels <hoendech@ecc.lu> writes:
> On 27 Nov 2006 15:56:11 GMT
> Casper H.S. Dik <Casper.Dik@Sun.COM> wrote:
>
>
> I think it's the standard that is confusing. Take the first sentence in
> the paragraph above - it says that both 0 and (void *)0 are called the
> null pointer constant,


You are misquoting the text, which does not talk about _the_ null
pointer constant, but _a_ null pointer constant ...

> so obviously they are the same,


.... which precisely means that 0 and (void *)null are both _called_ null
pointer constants. Apart from that, the type of an integer constant
with value 0 is int and the type of an integer constant with value 0
converted to void * is void *.

> and there should be no reason to prefer one notation to the
> other. Such a reason can be inferred from a number of more-or-less
> related paragraphs in the standard


The standard specifies exactly when an integer constant of value 0 is
automatically converted to a null pointer: If it is the right operand
of a simple assignment and the left operand has pointer type. Further,
the standard specifies as exaclty that each other pointer conversion
than the ones defined for simple assignments require explicit casts.

> (and cogitation on the obvious limitations of compilers),


That was just an attempt to formulate this simple fact in a way that
would be hopefully more accessible to the more simple-minded than the
statement of 'conversions to be applied when no prototype is
available', which Mr Lane (at least I hope that was his name) was so
kind to quote when trying to find a specification of something
completely unrelated (behaviour of variadic functions). An int is not
promoted and no automatic conversions except integer promotions are
supposed to happen in absence of a prototype. 0 stays 0 and on a LP64
machine, the code bombs out (I actually had the mispleasure to debug
that before I knew the specification).

> but as far as I can see (from admittedly only scanning the document) it's
> not unambiguously stated.


It is unambigously stated in each and every applicable paragraph, over
and over again (I think I quoted most of them).
Stefaan A Eeckels

2006-11-27, 7:23 pm

On Mon, 27 Nov 2006 19:18:44 +0100
Rainer Weikusat <rainer.weikusat@sncag.com> wrote:

> Stefaan A Eeckels <hoendech@ecc.lu> writes:
>
> You are misquoting the text, which does not talk about _the_ null
> pointer constant, but _a_ null pointer constant ...


Are you implying that there is more that one null pointer constant?

I believe I am not misquoting, for me at least the sentence says quite
clearly that 0 is a null pointer constant, and (void *)0 is a null
pointer constant. Unless there are several different null pointer
constants, it follows that 0 and (void *)0 are the same.

>
> ... which precisely means that 0 and (void *)null are both _called_
> null pointer constants.


Do you mean "null pointer constant" is something like a first name? Both
you and Rainer Maria Rilke are called Rainer, but you're not quite the
same person. 0 and (void *)0 are both called null pointer constants, but
they are different objects. There are two distinctly different null
pointer constants, confusingly called "null pointer constant". I see.
Sorry for sounding sarcastic.

> Apart from that, the type of an integer constant with value 0 is int
> and the type of an integer constant with value 0 converted to void *
> is void *.


But like 0, (void *)0 is also a null pointer constant, so the type of
(void *)0 must be int? Or is the type of 0 "int" and that of (void *)0
"a pointer to void", so they are different but still called the same?
Aha, maybe "int" and "pointer" are their surnames, so 0 is Ms "void
pointer constant int" and (void *)0 is Ms "void pointer constant
pointer". I see. Sorry for sounding sarcastic.

I think you're proving my point that the standard is confusing.

>
> The standard specifies exactly when an integer constant of value 0 is
> automatically converted to a null pointer: If it is the right operand
> of a simple assignment and the left operand has pointer type.


The standard equally says that an integer expression with value 0 is
called a void pointer constant. It says that the same integer expression
with value 0 cast to type void * is also called a void pointer constant.
If that is the case, the original and the cast are the same.

If the sentence would have read:

An integer constant expression with the value 0, cast to type
void *, is called a null pointer constant.

it would have been unambiguous. But when it says:

An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer constant.
If a null pointer constant is converted to a pointer type, the
resulting pointer, called a null pointer, [...]

it suggests that 0 and (void *)0 are the same.

Paragraph 6.3.2.3.3 thus confusingly suggests that (void *)0 is an
integer constant expression, by virtue of it being called exactly the
same as 0 (no different surnames like with Rainer Weikusat and Rainer
Rilke, not even a different middle initial , and by virtue of it
needing to be converted to a pointer to become the null pointer.

Leaving the confusing "or" part of the first sentence away, and writing
each definition the paragraph contains separately clearly shows the
ambiguity:

"An integer constant expression with the value 0 cast to type void * is
called a null pointer constant. If a null pointer constant is converted
to a pointer type, the resulting pointer, called a null pointer [...]"

and

"An integer constant expression with the value 0 is called a null
pointer constant. If a null pointer constant is converted to a pointer
type, the resulting pointer, called a null pointer [...]"

Something cast to (void *) is a pointer, and an integer constant
expression is an int. Paragraph 6.3.2.3.3 conflates pointers and
integers in the best K&R tradition .

--
Stefaan A Eeckels
--
A human being should be able to change a diaper, plan an invasion,
butcher a hog, conn a ship, design a building, write a sonnet, balance
accounts, build a wall, set a bone, comfort the dying, take orders,
give orders, cooperate, act alone, solve equations, analyze a new
problem, pitch manure, program a computer, cook a tasty meal, fight
efficiently, die gallantly.
Specialization is for insects. -- Robert A. Heinlein
Erik Max Francis

2006-11-27, 7:23 pm

Stefaan A Eeckels wrote:

> On Sun, 26 Nov 2006 14:05:46 -0800
> Erik Max Francis <max@alcyone.com> wrote:
>
>
> An integral constant expression with value 0, or the same cast to
> void*, may be converted, by a cast, by an assignment, or by a
> comparison, to a pointer of any type. Notice that the conversion can be
> done by assignment or comparison as well as by a cast.


You're mixing up the difference between a null pointer and the null
pointer constant. The null pointer constant in C is a constant integral
zero when found in a null pointer context. That will be translated to a
null pointer of the appropriate type at compile time.

However, that doesn't mean that assigning an all-bits zero value to a
pointer at runtime will result in a null pointer; indeed, the C Standard
makes no assumptions about what the actual bit pattern of a null pointer
is (and, indeed, it could even be different patterns for different
pointer types).

Surely you wouldn't expect, for instance, the following to result in a
valid null pointer::

int i = 0;
int *p = (int *) i;

The precise same thing happens when you deal with varargs or a lack of
prototypes. The compiler has no way of knowing that a 0 that appears in
source code is supposed to be a null pointer constant rather than an
int, because it lacks a pointer context at compile time.

> That's not correct - the compiler must perform argument promotion on
> the non-explicitly typed arguments, which should ensure that alignment
> issues (which could be a problem for the varargs macros) are taken care
> of.


ints are not promoted. Even if they _were_ promoted, what would they be
promoted to? The type of the argument is not known at compile time.

> I only scanned through the document, but I could not find a direct
> reference to the need for casting zero (NULL) to a pointer in variadic
> functions. Notice that I'm not advocating against casting - it's always
> better to make one's intentions clear and unambiguous. It's just that in
> the specific case of zero it seems to me that the standard requires the
> compiler to ensure it is treated as a pointer.


There's no direct reference because there only behavior which is defined
is not undefined. The sections you're quoting have to do with finding
null pointer constants. Varargs and prototypeless cases do not involve
null pointer constants, because the compiler has no way of knowing that
a pointer context is desired. That is why the explicit cast is required
in both cases, or undefined behavior results.

Consider a standard varargs circumstance, where we're going to use a
(char *) 0 to indicate when we should stop processing a list of strings::

int printList(const char *prefix, ...);

printList("test:", "A", "B", "C", "D", 0);

This is all the compiler sees when it has to decide what to do with the
0 integral constant here in the varargs function call. How could it
possibly know I mean (char *) 0 and not (int) 0? The answer should be
obvious: It can't. Since there is no pointer context here (the types
of varargs arguments could be any types, not just pointers), the 0 is
not a null pointer constant; it's an int constant. And hence it is
passed in as an int, which may or may not be the same size as a char *.
Even if it, there is no guarantee that the bit pattern for an int 0 is
the same the bit pattern for a null pointer of type char *. Hence it
invokes undefined behavior, and does so in a big way.

This is not some theoretical example. I've seen crashes on varargs
cases in weird architectures (like Macintoshes back in the day running
Think C) when lacking an explicit cast, since ints and pointer types had
different sizes.

The C FAQ goes into detail about this issue:

http://c-faq.com/null/nullreq.html

--
Erik Max Francis && max@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM, Y!M erikmaxfrancis
Dear World: I am leaving because I am bored.
-- George Sanders (in his suicide note)
Erik Max Francis

2006-11-27, 7:23 pm

Rainer Weikusat wrote:

> Erik Max Francis <max@alcyone.com> writes:
>
> [...]
>
>
> This is not true. The cast is necessary if you use 0 and ints and
> pointers are not basically the same thing (eg LP64) for your
> implementation or if you use NULL and that expands to something which
> does not have a type compatible with the expected pointer type (which
> again depends on the implementation).


Not including the cast invokes undefined behavior, whether or not it
just so happens to work right on your particular architecture with your
particular compiler today.

The explicit cast _is_ always necessary to write portable code.

--
Erik Max Francis && max@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM, Y!M erikmaxfrancis
Dear World: I am leaving because I am bored.
-- George Sanders (in his suicide note)
Stefaan A Eeckels

2006-11-28, 7:30 am

On Mon, 27 Nov 2006 16:03:42 -0800
Erik Max Francis <max@alcyone.com> wrote:

> Stefaan A Eeckels wrote:
>
>
> You're mixing up the difference between a null pointer and the null
> pointer constant. The null pointer constant in C is a constant
> integral zero when found in a null pointer context. That will be
> translated to a null pointer of the appropriate type at compile time.


I am not confusing the null pointer constant with the null pointer, the
standard is (Paragraph 6.3.2.3.3):

An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer constant.
If a null pointer constant is converted to a pointer type, the
resulting pointer, called a null pointer, [...]

This paragraph says either that 0 is a pointer, or that (void *)0 is an
integer constant. If the null pointer constant is an integer (meaning
that it must be cast to be a pointer), then (void *)0 is not
a pointer. If (void *)0 is a pointer, then it follows that 0 is also a
pointer, or they couldn't be both null pointer constants.

> However, that doesn't mean that assigning an all-bits zero value to a
> pointer at runtime will result in a null pointer; indeed, the C
> Standard makes no assumptions about what the actual bit pattern of a
> null pointer is (and, indeed, it could even be different patterns for
> different pointer types).
>
> Surely you wouldn't expect, for instance, the following to result in
> a valid null pointer::
>
> int i = 0;
> int *p = (int *) i;


With an (int *) and a variable I most certainly do not expect that.
We're talking about constant expressions, i.e. expressions that can be
evaluated at compile time.

But I expect (void *)0 to be a pointer, and not an integer,
constant expression or not. 6.2.5.26 specifically mentions that:

26 A pointer to void shall have the same representation and
alignment requirements as a pointer to a character type.(39)
Similarly, pointers to qualified or unqualified versions of
compatible types shall have the same representation and alignment
requirements. All pointers to structure types shall have the same
representation and alignment requirements as each other. All pointers
to union types shall have the same representation and alignment
requirements as each other. Pointers to other types need not have the
same representation or alignment requirements.

with footnote 39 stating:

39) The same representation and alignment requirements are meant to
imply interchangeability as arguments to functions, return values
from functions, and members of unions.

Thus, (void *)0 and (char *)0 should be interchangeable in variadic
function calls, and because 6.3.2.3.3 says:

An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer constant.

one should be able to use 0 and (void *)0 interchangeably. Notice that
because we're talking about constant expressions, the compiler _knows_
that it is dealing with "0" and hence can make special arrangements to
ensure that it will be (possibly at run time through special code)
interpreted as a null pointer if appropriate.

The way it is written, paragraph 6.3.2.3.3 to me means that the compiler
should at all times assure that a 0 is the same as (void *)0. If that is
impossible, then 6.3.2.3.3 is erroneous and should be reformulated, for
example by no longer stating that 0 is a null pointer constant.

The equivalence of 0 and the null pointer stems from K&R 'C', and it
might be that the committee tried to maintain that equivalence while
introducing a more formal null pointer. It seems that the committee also
tried to make variadic functions part of the language specification (the
ellipsis) while maintaining the hackish implementation of K&R
'C' variadic functions. I believe that the result is a bit of a muddle.

That being said, it sure is good practice to cast in case of doubt. The
standard, however, contains indications that, for 0 used as the null
pointer, this should not be required. It certainly doesn't seem to
clearly state that 0 _should_ be cast to the expected pointer type
(such as char *), while it does clearly state that (char *) and (void *)
should have the same representation and alignment and thus should be
interchangeable, even in variadic contexts. The void pointer should
thus never have to be cast to a char pointer.

--
Stefaan A Eeckels
--
"Object-oriented programming is an exceptionally bad idea which
could only have originated in California." --Edsger Dijkstra
Geoff Clare

2006-11-28, 1:18 pm

Rainer Weikusat <rainer.weikusat@sncag.com> wrote, on Mon, 27 Nov 2006:
> Geoff Clare <geoff@clare.See-My-Signature.invalid> writes:
>
> [...]
>
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^
>
> Obnoxious emphasis added, because that appears to be something you have
> missed up to now.


I didn't miss it. My whole point was that your assumption was
incorrect.

This side discussion doesn't seem to be going anywhere. I suggest
that we just agree to disagree. The purpose of my first post in
this thread (to ensure readers of the newsgroup are aware that NULL
needs to be cast when used as the null terminator in an execl*()
call) has long since been achieved.

--
Geoff Clare <netnews@gclare.org.uk>

Erik Max Francis

2006-11-28, 7:25 pm

Stefaan A Eeckels wrote:

> I am not confusing the null pointer constant with the null pointer, the
> standard is (Paragraph 6.3.2.3.3):
>
> An integer constant expression with the value 0, or such an
> expression cast to type void *, is called a null pointer constant.
> If a null pointer constant is converted to a pointer type, the
> resulting pointer, called a null pointer, [...]
>
> This paragraph says either that 0 is a pointer, or that (void *)0 is an
> integer constant. If the null pointer constant is an integer (meaning
> that it must be cast to be a pointer), then (void *)0 is not
> a pointer. If (void *)0 is a pointer, then it follows that 0 is also a
> pointer, or they couldn't be both null pointer constants.


No one is disputing that (void *) 0 is clearly a valid null pointer
constant in all circumstances, so let's leave that aside. The issue is
with an unadorned integral zero constant like 0 or 0L or (1 - 1). These
_can be_ null pointer constants, but only when they appear in a
pointer context. For instance, you don't really think that the
following is a null pointer constant, do you:

int i = 0;

Clearly it is not, because it does not appear in a pointer context.
This one, however, obviously does:

void *p = 0;

The issue where the cast is required, regardless of whether you use 0 or
NULL, is when that context cannot be clear to the compiler. The
examples of these cases are varargs or missing-prototype environments,
where an integral constant cannot be deduced by the compiler to
represent a null pointer constant, and thus simply assumes that it is an
integer literal like it usually is.

ints and pointers need not have the same size on any given architecture
(and on some, they don't). So what does 0 mean? Either it's an int or
a null pointer, right? So when making a varargs or prototype-less
call, how can the compiler know which one it is? The answer is it
can't, which is why the cast is required.

int f();
f(0);

At the time of this call, the prototype of f is not known. Does this
pass an int 0 or a null pointer? It can't be both.

> Thus, (void *)0 and (char *)0 should be interchangeable in variadic
> function calls, ...


That is correct. Though note that it taking a char * was only an
example. It could be used as any pointer type on the other side of the
call.

> ... and because 6.3.2.3.3 says:
>
> An integer constant expression with the value 0, or such an
> expression cast to type void *, is called a null pointer constant.
>
> one should be able to use 0 and (void *)0 interchangeably.


Where in that statement does it guarantee that 0 and (void *) 0 are
always interchangeable? It doesn't make that guarantee at all, it just
says they're both null pointer constants (under some circumstances).

> The way it is written, paragraph 6.3.2.3.3 to me means that the compiler
> should at all times assure that a 0 is the same as (void *)0. If that is
> impossible, then 6.3.2.3.3 is erroneous and should be reformulated, for
> example by no longer stating that 0 is a null pointer constant.


It does not logically mean that. It means that they're both null
pointer constants, not that they are always precisely the same thing
(because, well, they're not, since they're written differently).

> That being said, it sure is good practice to cast in case of doubt. The
> standard, however, contains indications that, for 0 used as the null
> pointer, this should not be required. It certainly doesn't seem to
> clearly state that 0 _should_ be cast to the expected pointer type
> (such as char *), while it does clearly state that (char *) and (void *)
> should have the same representation and alignment and thus should be
> interchangeable, even in variadic contexts. The void pointer should
> thus never have to be cast to a char pointer.


The cast _is_ required. As I pointed out, this is not academic; certain
architectures _will_ fail if the case is not included in varargs calls.
Again, see the C FAQ which goes into detail on this issue.

--
Erik Max Francis && max@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM, Y!M erikmaxfrancis
In this world, nothing is certain but death and taxes.
-- Benjamin Franklin
Stefaan A Eeckels

2006-11-28, 7:25 pm

On Tue, 28 Nov 2006 12:13:34 -0800
Erik Max Francis <max@alcyone.com> wrote:

> Stefaan A Eeckels wrote:
>
>
> No one is disputing that (void *) 0 is clearly a valid null pointer
> constant in all circumstances, so let's leave that aside. The issue
> is with an unadorned integral zero constant like 0 or 0L or (1 - 1).
> These _can be_ null pointer constants, but only when they appear in a
> pointer context.


But the section I quoted does not say that, and there are as far as I
can see no other sections that say that 0 is only a null pointer
constant in a pointer context. It might be obvious to those with a 'C'
background, but if I were to write a compiler from scratch, paragraph
6.3.2.3.3 does not make clear that there is a difference between 0 and
(void *)0.

> For instance, you don't really think that the
> following is a null pointer constant, do you:
>
> int i = 0;
>
> Clearly it is not, because it does not appear in a pointer context.
> This one, however, obviously does:
>
> void *p = 0;
>
> The issue where the cast is required, regardless of whether you use 0
> or NULL, is when that context cannot be clear to the compiler. The
> examples of these cases are varargs or missing-prototype
> environments, where an integral constant cannot be deduced by the
> compiler to represent a null pointer constant, and thus simply
> assumes that it is an integer literal like it usually is.


I understand the limitations of current compilers, and I know that
the compiler cannot determine the type of the arguments to a variadic
funtion. That is not the issue. The issue is that the standard does not
clearly and unambiguously state that 0 _should_ be cast to (void *) to
be a pointer to void.

> ints and pointers need not have the same size on any given
> architecture (and on some, they don't). So what does 0 mean? Either
> it's an int or a null pointer, right? So when making a varargs or
> prototype-less call, how can the compiler know which one it is? The
> answer is it can't, which is why the cast is required.


Sure it can - after all it's just a symbol in the source code, and the
compiler can make provision for correct execution in those cases that
there are differences between pointers and integers, at the price of
run-time code to do the appropriate conversion.

> int f();
> f(0);
>
> At the time of this call, the prototype of f is not known. Does this
> pass an int 0 or a null pointer? It can't be both.


Of course not. But all these technical considerations are not germane
to the question of what the standard says, and whether or not the
standard is unambiguous.

>
> That is correct. Though note that it taking a char * was only an
> example. It could be used as any pointer type on the other side of
> the call.


Hmm, I'm not sure I understand you, but if you mean that (void *)0 could
be used instead of (int *)0 in a variadic function, the standard is
again not clear on the issue. Look at paragraph 6.2.5.26 which says
that _only_ (char *) and (void *) have the same representation and
alignment requirements.

Of course, the standard also says that any pointer can be converted to
a void pointer and back (6.3.2.3.1), and that a null pointer of
whatever type is guaranteed to compare unequal to a pointer to any
object or function (6.3.2.3.3), but at the same time 6.2.5.26 says that
_only_ (char *) and (void *) are really "identical", without clarifying
the implications of this on the null pointer.

Clearly, if a (int *) does not have the same representation and
alignment requirements as a (void *), but still needs to convert to a
(void *) and back, the compiler will have to issue code to ensure that
these requirements are met.

Thus, if the standard happens to say that 0 and (void *)0 are both the
(a) null pointer constant, a 'C' implementation on a processor where
this is difficult (for example because (void *)0 must be contrived
because the machine uses the first 256 byte page for relative
addressing, and uses one's complement arithmetic and thus has both a
positive and negative zero(*)) it would be no reason to deviate from
the standard.

(*) like the Control Data Cyber 17, which I had the dubious pleasure to
write assembly code for.

>
> Where in that statement does it guarantee that 0 and (void *) 0 are
> always interchangeable? It doesn't make that guarantee at all, it
> just says they're both null pointer constants (under some
> circumstances).


Where does it say "under some circumstances?". The sentence is quite
clear, and makes the following two statements:

1. An integer constant expression with the value 0 is called a null
pointer constant.

2. An integer constant expression with the value 0 cast to type void *
is called a null pointer constant.

>
> It does not logically mean that. It means that they're both null
> pointer constants, not that they are always precisely the same thing
> (because, well, they're not, since they're written differently).


Logically, if a == c and b == c then a == b.

What you suggest is that there are two different null pointer
constants: if 0 is not precisely the same as (void *)0 and both 0 and
(void *)0 are null pointer constants, then it follows that "null
pointer constant" is an ambiguous concept (and "are called" is being
used in the same sense as "the sons of Max and Stefaan are both called
Fred"):

If this is the case, the differences between the two different things
both called "null pointer constant" are never mentioned in the standard.
However you look at it, the 'C' standard is ambiguous.

>
> The cast _is_ required. As I pointed out, this is not academic;
> certain architectures _will_ fail if the case is not included in
> varargs calls. Again, see the C FAQ which goes into detail on this
> issue.


If programs malfunction when (void *)0 is used instead of (char *)0,
then the compiler is not conforming (see 6.2.5.26 which, clearly for
once, stipulates that (void *) and (char *) have the same
representation and alignment requirements).

--
Stefaan A Eeckels
--
Every modification you make to a third-party software product is just
one more thing that you have to document, maintain, reimplement with
upgrades, and explain to your co-workers. Most of the time this is a
very bad idea. -- Russ Allbery (rra@stanford.edu)
Erik Max Francis

2006-11-29, 1:30 am

Stefaan A Eeckels wrote:

> But the section I quoted does not say that, and there are as far as I
> can see no other sections that say that 0 is only a null pointer
> constant in a pointer context. It might be obvious to those with a 'C'
> background, but if I were to write a compiler from scratch, paragraph
> 6.3.2.3.3 does not make clear that there is a difference between 0 and
> (void *)0.


It doesn't say there's a difference, but there obviously is one. You
acknowledge below that

int i = 0;

clearly is an integral constant zero, not a null pointer constant, so
obviously there _is_ a difference between 0 and (void *) 0, because
while the latter is always a null pointer constant, the former only is
_sometimes_.

The cases where an unadorned 0 represents a null pointer constant as
opposed to its usual meaning of a literal integer zero are given on a
case by case basis. Just search the Standard for "null pointer
constant." Examples are 6.5.9/2, 6.5.9/5, 6.5.15/2, 6.5.16.1/1, and 6.6/7.

> I understand the limitations of current compilers, and I know that
> the compiler cannot determine the type of the arguments to a variadic
> funtion. That is not the issue. The issue is that the standard does not
> clearly and unambiguously state that 0 _should_ be cast to (void *) to
> be a pointer to void.


It doesn't say it directly, but it indicates it because it shows the
circumstances where null pointer constants are recognized as such.
Varargs and prototype-less function calls are not examples.

> Sure it can - after all it's just a symbol in the source code, and the
> compiler can make provision for correct execution in those cases that
> there are differences between pointers and integers, at the price of
> run-time code to do the appropriate conversion.


This is not done in the C language at all, for obvious reasons of
efficiency.

> Hmm, I'm not sure I understand you, but if you mean that (void *)0 could
> be used instead of (int *)0 in a variadic function, the standard is
> again not clear on the issue. Look at paragraph 6.2.5.26 which says
> that _only_ (char *) and (void *) have the same representation and
> alignment requirements.


Yes, that is what I mean. But that is _only_ true of void * and char *.
It is not true of void * and other data pointers. Thus, even using
(void *) 0 is not sufficient in varargs or prototype-less function
calls, because there is no guarantee in the Standard that the pointers
will have the same internal representation or even size. And, since the
compiler can't determine that a (void *) 0 which appears at the function
call is supposed to be a, say, int * when used in the varargs, the
explicit cast is required.

> Of course, the standard also says that any pointer can be converted to
> a void pointer and back (6.3.2.3.1), and that a null pointer of
> whatever type is guaranteed to compare unequal to a pointer to any
> object or function (6.3.2.3.3), but at the same time 6.2.5.26 says that
> _only_ (char *) and (void *) are really "identical", without clarifying
> the implications of this on the null pointer.
>
> Clearly, if a (int *) does not have the same representation and
> alignment requirements as a (void *), but still needs to convert to a
> (void *) and back, the compiler will have to issue code to ensure that
> these requirements are met.


And it can't do that when it doesn't know what the ultimate pointer type
will be (and it isn't char *). That's why the explicit cast to the
proper pointer type is required for varargs and prototype-less function
calls; even (void *) 0 will not suffice there.

> Thus, if the standard happens to say that 0 and (void *)0 are both the
> (a) null pointer constant, a 'C' implementation on a processor where
> this is difficult (for example because (void *)0 must be contrived
> because the machine uses the first 256 byte page for relative
> addressing, and uses one's complement arithmetic and thus has both a
> positive and negative zero(*)) it would be no reason to deviate from
> the standard.


You're mixing up null pointers and null pointer constants again. The
actual bit pattern of a null pointer doesn't have anything to do with 0.
It may not be all-bits zero. It may even be different for different
types of pointers.

> Where does it say "under some circumstances?". The sentence is quite
> clear, and makes the following two statements:
>
> 1. An integer constant expression with the value 0 is called a null
> pointer constant.
>
> 2. An integer constant expression with the value 0 cast to type void *
> is called a null pointer constant.


Yes. But note that it doesn't say "0 is a null pointer constant and
never anything else."

> What you suggest is that there are two different null pointer
> constants: if 0 is not precisely the same as (void *)0 and both 0 and
> (void *)0 are null pointer constants, then it follows that "null
> pointer constant" is an ambiguous concept (and "are called" is being
> used in the same sense as "the sons of Max and Stefaan are both called
> Fred"):


0 is a null pointer constant. It is also an int literal. (void *) is a
null pointer constant, but it is not an int literal. That is a pretty
clear example that they are not completely equivalent.

> If this is the case, the differences between the two different things
> both called "null pointer constant" are never mentioned in the standard.
> However you look at it, the 'C' standard is ambiguous.


It is not ambiguous. You're simply not reading the whole thing. It
defines 0 (and (void *) 0) as a null pointer constant, and then invokes
situations where a null pointer as part of an expression has a different
effect than an int literal. It never says that 0 and (void *) 0 are
precisely equivalent, or that 0 is never anything else (something which
would be pretty absurd, anyway).

This is not uncommon in standards. A sequence of tokens is defined as
having special significance, and then as appropriate, when that context
comes up, its significance takes precedence.

> If programs malfunction when (void *)0 is used instead of (char *)0,
> then the compiler is not conforming (see 6.2.5.26 which, clearly for
> once, stipulates that (void *) and (char *) have the same
> representation and alignment requirements).


I was only giving char * as an example. If the desired type on the
other end of the function call is some other pointer type than a void *
or a char *, the explicit cast to the appropriate type is required. If
it is a char * or a void *, then it still must be explicitly cast, but
it can be cast to either a char * or a void *, since they are guaranteed
to have the same size and alignment.

In the general case, the proper cast to the appropriate type is
required. char * and void * are special cases, but still, an explicit
cast to _something_ compatible is required.

--
Erik Max Francis && max@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM, Y!M erikmaxfrancis
Throw me money / I'll live forever
-- Nik Kershaw
Stefaan A Eeckels

2006-11-29, 7:28 am

On Tue, 28 Nov 2006 22:43:36 -0800
Erik Max Francis <max@alcyone.com> wrote:

> Stefaan A Eeckels wrote:
>
>
> It doesn't say there's a difference, but there obviously is one. You
> acknowledge below that
>
> int i = 0;
>
> clearly is an integral constant zero, not a null pointer constant, so
> obviously there _is_ a difference between 0 and (void *) 0, because
> while the latter is always a null pointer constant, the former only
> is _sometimes_.


I am not arguing that there is no difference between 0 and (void *)0.
What I am saying is that the paragraph under consideration says that 0
is a null pointer constant. It also says that (void *)0 is a null
pointer constant. It does _not_ say that 0 is the null pointer
constant under certain circumstances, and (void *)0 is the null pointer
constant under other circumstances.

> The cases where an unadorned 0 represents a null pointer constant as
> opposed to its usual meaning of a literal integer zero are given on a
> case by case basis. Just search the Standard for "null pointer
> constant." Examples are 6.5.9/2, 6.5.9/5, 6.5.15/2, 6.5.16.1/1, and
> 6.6/7.
>
>
> It doesn't say it directly, but it indicates it because it shows the
> circumstances where null pointer constants are recognized as such.
> Varargs and prototype-less function calls are not examples.
>
>
> This is not done in the C language at all, for obvious reasons of
> efficiency.


I know (hell, I've been using 'C' on a Z-80) that 'C' tries to be
efficient - but this is not something that the standard defines (it
never says: "if something in this standard cannot be implemented
efficiently, it can be disregarded").
>
>
> Yes, that is what I mean. But that is _only_ true of void * and char
> *. It is not true of void * and other data pointers. Thus, even
> using (void *) 0 is not sufficient in varargs or prototype-less
> function calls, because there is no guarantee in the Standard that
> the pointers will have the same internal representation or even
> size. And, since the compiler can't determine that a (void *) 0
> which appears at the function call is supposed to be a, say, int *
> when used in the varargs, the explicit cast is required.


>
>
> And it can't do that when it doesn't know what the ultimate pointer
> type will be (and it isn't char *). That's why the explicit cast to
> the proper pointer type is required for varargs and prototype-less
> function calls; even (void *) 0 will not suffice there.


Sure it can do that - it just takes some additional data structures and
some code. It's not efficient on architectures that have different
sizes for pointers to integers and pointers to characters, but unlike
the informal K&R 'C' specification, the ISO 'C' specification is
supposed to define exactly what can be relied upon in a conforming
compiler, and what is machine dependent or undefined.

>
> You're mixing up null pointers and null pointer constants again. The
> actual bit pattern of a null pointer doesn't have anything to do with
> 0. It may not be all-bits zero. It may even