Definition of NULL

J

Jason Curl

I've been reading this newsgroup for some time and now I am thoroughly
confused over what NULL means.

I've read a NULL pointer is zero (or zero typecast as a void pointer),
others say it's compiler dependent (and that NULL might be anything, but
it is always NULL).

The source snippet is below. The question is:
- When I use calloc to allocate a block of memory, preinitialising it to
zero, is this equivalent (and portable C) to iterating through the
structure 128 times setting the individual elements to NULL (i.e. is
myFunc() and myFunc2() equivalent in functionality)?



struct _KeySequence {
KeyCallback *cb;
struct _KeySequence *next;
};

struct _KeySequence *node;

int myFunc(void)
{
node = calloc(128, sizeof(struct _KeySequence));
if (node == NULL) {
return 0;
} else {
return 1;
}
}

int myFunc2(void)
{
int j;

node = malloc(128 * sizeof(struct _KeySequence));
if (node == NULL) {
return 0;
} else {
for (j=0; j<127; j++) {
node[j].cb = NULL;
node[j].next = NULL;
}
return 1;
}
}
 
T

Tatu Portin

Jason said:
I've been reading this newsgroup for some time and now I am thoroughly
confused over what NULL means.

I've read a NULL pointer is zero (or zero typecast as a void pointer),
others say it's compiler dependent (and that NULL might be anything, but
it is always NULL).

NULL is set zero by some compilers, when the machine interprets zero as
'pointer to nothing'. NULL is set to an other value by compiler, if the current
machine interprets some other value as 'pointer to nothing'.
So it depends on machine you are compiling for what value NULL gets.

This is something I remember I have read from DJGPP Faq. Check it out.
 
K

Keith Thompson

Jason Curl said:
I've been reading this newsgroup for some time and now I am thoroughly
confused over what NULL means.

NULL is a macro that expands to a null pointer constant, an expression
that yields a null pointer when evaluated. It's better to refer to a
"null pointer" rather than a "NULL pointer".
I've read a NULL pointer is zero (or zero typecast as a void pointer),
others say it's compiler dependent (and that NULL might be anything,
but it is always NULL).

The source snippet is below. The question is:
- When I use calloc to allocate a block of memory, preinitialising it
to zero, is this equivalent (and portable C) to iterating through the
structure 128 times setting the individual elements to NULL (i.e. is
myFunc() and myFunc2() equivalent in functionality)?
[snip]

No, using calloc() (or memset(), or whatever) to set a pointer to
all-bits-zero is not guaranteed to set it to a null pointer, though it
is likely to do so on many implementations.

The C FAQ is at <http://www.eskimo.com/~scs/C-faq/top.html>. Read
section 5. Come back if you're still confused.
 
G

Gordon Burditt

Jason Curl said:
I've been reading this newsgroup for some time and now I am thoroughly
confused over what NULL means.

I've read a NULL pointer is zero (or zero typecast as a void pointer),

If you want to set a pointer to NULL, there are various ways to do
it:

char *p;

p = NULL;
p = 0;
p = (void *) 0;
others say it's compiler dependent (and that NULL might be anything, but
it is always NULL).

The internal representation (bit pattern) of a NULL is not specified.
It is *NOT* guaranteed to be 0xdeadbeef, not even on 32-bit machines.
But if the internal representation is 0xdeadbeef,
p == 0
must be true if p contains a NULL pointer. If that means generating
assembly-language code to compare p to the bit pattern 0xdeadbeef, so
be it.

However, neither
memset((void *)&p, 0, sizeof(p));
nor
memset((void *)&p, 0xdeadbeef, sizeof(p));
is guaranteed to set p to a NULL pointer.


The source snippet is below. The question is:
- When I use calloc to allocate a block of memory, preinitialising it to
zero, is this equivalent (and portable C) to iterating through the
structure 128 times setting the individual elements to NULL (i.e. is
myFunc() and myFunc2() equivalent in functionality)?

*NO*. If you assign 0 to a pointer variable *AS* a pointer variable,
you get a NULL pointer. If you assign 0 to a bunch of bytes making
up a pointer variable, you may or may not get a NULL pointer.

Gordon L. Burditt
 
K

Keith Thompson

The internal representation (bit pattern) of a NULL is not specified.
It is *NOT* guaranteed to be 0xdeadbeef, not even on 32-bit machines.
But if the internal representation is 0xdeadbeef,
p == 0
must be true if p contains a NULL pointer. If that means generating
assembly-language code to compare p to the bit pattern 0xdeadbeef, so
be it.

However, neither
memset((void *)&p, 0, sizeof(p));
nor
memset((void *)&p, 0xdeadbeef, sizeof(p));
is guaranteed to set p to a NULL pointer.

The latter almost certainly won't, even if a null pointer is
represented as 0xdeadbeef. The second argument to memset() is
converted to unsigne char. On system with CHAR_BIT==8, this will most
likely (or certainly, but I'm too lazy to track the chapter(s) and
verse(s)) give you 0xef (the low-order 8 bits). If sizeof(p) is 4,
the value stored in p is going to be 0xefefefef.

(If CHAR_BIT==32, it should assign 0xdeadbeef to p, but that would be
an unusual system.)

Again, null pointers are explained quite well in section 5 of the
C FAQ (google "C FAQ"). There's little point in rehashing the
explanations here.
 
M

Malcolm

Jason Curl said:
The source snippet is below. The question is:
- When I use calloc to allocate a block of memory, preinitialising it to
zero, is this equivalent (and portable C) to iterating through the
structure 128 times setting the individual elements to NULL (i.e. is
myFunc() and myFunc2() equivalent in functionality)?
No it isn't. On the vast majority of machines, NULL is all bits zero, so
calling calloc() to return an array of pointers will give you null pointers.
However it isn't guaranteed, in case some oddball machine out there uses all
bits zero as a trap representation or something.
 
J

Jack Klein

NULL is set zero by some compilers, when the machine interprets zero as
'pointer to nothing'. NULL is set to an other value by compiler, if the current
machine interprets some other value as 'pointer to nothing'.

No, you are quite wrong. NULL is a macro, not a pointer at all. The
macro NULL is "an implementation defined null pointer constant". It
is required by the C standard to evaluate to one of the two following:

1. An integer constant expression that evaluates to 0.

2. An expression of type 1 case to pointer to void.

That means acceptable values are: 0 or (void *)0.

This has nothing at all to do with the bit representation of a null
(not NULL) pointer, which may or may not be all bits 0.
So it depends on machine you are compiling for what value NULL gets.

No, the machine for which you are compiling has nothing at all to do
with the macro NULL, which must always be equivalent to one of the two
forms above. It does define the bit pattern of a null pointer, but
that has nothing to do with the macro itself.
This is something I remember I have read from DJGPP Faq. Check it out.

I am almost certain that you are misremembering or misunderstanding
the DJGPP FAQ. Most likely you are confusing the difference between
the macro NULL and the representation of a null pointer, which are
different but related things.
 
P

pete

Jason said:
I've been reading this newsgroup for some time and now I am thoroughly
confused over what NULL means.

null character
null pointer constant
null pointer
NULL


"null character"
N869
5.2.1.2 Multibyte characters
-- A byte with all bits zero shall be interpreted as a
null character independent of shift state.
6.4.4.4 Character constants
[#12] EXAMPLE 1 The construction '\0' is commonly used to
represent the null character.


"null pointer constant" and "null pointer"
N869
6.3.2.3 Pointers
[#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.

"NULL"
N869
7.17 Common definitions <stddef.h>
[#3] The macros are
NULL
which expands to an implementation-defined null pointer
constant
 
P

pete

pete wrote:
"null character"
N869
5.2.1.2 Multibyte characters

This would probably be a more better quote:
N869
5.2 Environmental considerations
5.2.1 Character sets
[#2]
A byte with all bits set to 0,
called the null character, shall exist in the basic
execution character set; it is used to terminate a character
string.
 
J

Jason Curl

Keith said:
Jason Curl said:
I've been reading this newsgroup for some time and now I am thoroughly
confused over what NULL means.


NULL is a macro that expands to a null pointer constant, an expression
that yields a null pointer when evaluated. It's better to refer to a
"null pointer" rather than a "NULL pointer".

I've read a NULL pointer is zero (or zero typecast as a void pointer),
others say it's compiler dependent (and that NULL might be anything,
but it is always NULL).

The source snippet is below. The question is:
- When I use calloc to allocate a block of memory, preinitialising it
to zero, is this equivalent (and portable C) to iterating through the
structure 128 times setting the individual elements to NULL (i.e. is
myFunc() and myFunc2() equivalent in functionality)?

[snip]

No, using calloc() (or memset(), or whatever) to set a pointer to
all-bits-zero is not guaranteed to set it to a null pointer, though it
is likely to do so on many implementations.

The C FAQ is at <http://www.eskimo.com/~scs/C-faq/top.html>. Read
section 5. Come back if you're still confused.
The FAQ is useful, Q7.13 directly says what I'm doing is wrong.

I understand that 0 in a ptr context is NULL and that ANSI also allows
(void *)0 as an interpretation of NULL. I also understand that NULL for
an (int *) can be internally different to (char *) (Q5.17).

What I don't understand is why typecasting a NULL is not necessary.

For example:
a) int *p = NULL;
b) execl("/bin/sh", "sh", "-c", "date", NULL);

So (a) is OK, as the compiler can determine that NULL (if 0) should be
(int *)0. But for (b) I get the impression that it is incorrect and
should really be:

execl("/bin/sh", "sh", "-c", "date", (char *)NULL);

or is this wrong too, and it should be as in Q5.2?
 
C

Charlie Gordon

The source snippet is below. The question is:
- When I use calloc to allocate a block of memory, preinitialising it to
zero, is this equivalent (and portable C) to iterating through the
structure 128 times setting the individual elements to NULL (i.e. is
myFunc() and myFunc2() equivalent in functionality)?

struct _KeySequence {
KeyCallback *cb;
struct _KeySequence *next;
};

struct _KeySequence *node;

int myFunc2(void)
{
int j;

node = malloc(128 * sizeof(struct _KeySequence));
if (node == NULL) {
return 0;
} else {
for (j=0; j<127; j++) { <<<< off by one error
node[j].cb = NULL;
node[j].next = NULL;
}
return 1;
}
}

Hi Jason,

The question about equivalence of setting pointers to NULL vs: initializing the
array to all bits zero has been addressed thoroughly in other posts, but no one
mentioned another reason why the initialization loop is not equivalent in your
example to the calloc approach.

You don't loop over all the elements in the array, as you made an off by one
error :
for (j = 0; j < 127; j++) loops over 127 elements and leaves the last one
(at offset 127) untouched !

As a rule of thumb, you should never have to manipulate/compute off by one
numbers, nor use the <= operator.
Common C idioms make this unnecessary :

for (i = 0; i < n; i++) { ... } loops over n elements upwards
for (i = n; i-- > 0;) { ... } loops over n elements downwards
or possibly
for (int i = n; --i >= 0; ) { ... } but only works on signed i !

functions that take a buffer size should not be passed (sizeof(buf)-1), and
should be written as such.

Chqrlie.
 
M

Michael Mair

Jason said:
Keith Thompson wrote: [snip: Question answered]
The C FAQ is at <http://www.eskimo.com/~scs/C-faq/top.html>. Read
section 5. Come back if you're still confused.
The FAQ is useful, Q7.13 directly says what I'm doing is wrong.

I understand that 0 in a ptr context is NULL and that ANSI also allows
(void *)0 as an interpretation of NULL. I also understand that NULL for
an (int *) can be internally different to (char *) (Q5.17).

What I don't understand is why typecasting a NULL is not necessary.

Think 0. You can always assign 0 to any pointer, making clear that
this pointer does not point to anything at all. It is the compiler's
job to replace this 0 by whatever denotes a null pointer
representation in this case.

For example:
a) int *p = NULL;
b) execl("/bin/sh", "sh", "-c", "date", NULL);

So (a) is OK, as the compiler can determine that NULL (if 0) should be
(int *)0. But for (b) I get the impression that it is incorrect and
should really be:

execl("/bin/sh", "sh", "-c", "date", (char *)NULL);

or is this wrong too, and it should be as in Q5.2?

Is it clear to you if you think of 0 instead of (void *) 0?
If you give a prototype of that function, the compiler _knows_
the respective parameter type. Casting it is only an optical
help for you.
Actually, I am not sure about the case when you give only
a declaration but no prototype. Will look that up later today
if no one else is faster.

You can convert any pointer type into a void *; and you can
revert to the original pointer type. The 0 denotes a null pointer
in the respective context and "fits" the null pointer for the
respective type.


Cheers
Michael
 
L

Lawrence Kirby

NULL is a macro that expands to a null pointer constant,
Yes

an expression
that yields a null pointer when evaluated.

No, 0 is a valid null pointer constant but when evaluated you get
the int value 0, not a pointer at all and specifically not a null
pointer.

A null pointer constant can be *converted* (including implicitly without
a cast) to any pointer type and the result is a null pointer of that type.
It can also be compared to a pointer of any type and will compare equal
if the value of the pointer is a null pointer.
It's better to refer to a
"null pointer" rather than a "NULL pointer".

NULL is a macro defined in various standard headers that expands to a null
pointer constant. The form of a null pointer constant is an integer
constant expression with the value 0, or the same cast to void *.
The source snippet is below. The question is: - When I use calloc to
allocate a block of memory, preinitialising it to zero, is this
equivalent (and portable C) to iterating through the structure 128
times setting the individual elements to NULL (i.e. is myFunc() and
myFunc2() equivalent in functionality)?
[snip]

You have to make a clear distinction between how a null pointer is
specified in source code and how it is represented in an object at
runtime. C doesn't specify how a null pointer is represented in an object
at runtime; it doesn't have to be all-bits-zero. If it isn't it is up to
the compiler to supply the correct bit pattern when it sees a null pointer
being generated in the source code.

If you think it is odd that something that is derived from an integer
value of 0 in the source code may end up with a non-zero representation,
consider that type conversions (in this case converting to a pointer type)
are entirely at liberty to change representation. An obvious case is
converting to floating point where the result can have a significantly
different bit pattern and, again zero doesn't have to be represented as
all-bits-zero.

Lawrence
 
L

Lawrence Kirby

On Wed, 17 Nov 2004 10:18:59 +0100, Jason Curl wrote:

....
The FAQ is useful, Q7.13 directly says what I'm doing is wrong.

I understand that 0 in a ptr context is NULL

0, when converted to a pointer type, results in a null pointer of that
type. NULL is a macro.
and that ANSI also allows
(void *)0 as an interpretation of NULL.

It allows NULL to be defined as that.
I also understand that NULL for
an (int *) can be internally different to (char *) (Q5.17).
True.

What I don't understand is why typecasting a NULL is not necessary.

For example:
a) int *p = NULL;
b) execl("/bin/sh", "sh", "-c", "date", NULL);

So (a) is OK, as the compiler can determine that NULL (if 0) should be
(int *)0. But for (b) I get the impression that it is incorrect and
should really be:

execl("/bin/sh", "sh", "-c", "date", (char *)NULL);

execl() isn't defined by C but taking this to be an example of the use of
NULL in a variable argument list then, yes, the cast is needed there,
because it is up to the caller code to ensure the argument value has the
correct type.

Lawrence
 
R

Richard Bos

Jason Curl said:
The FAQ is useful, Q7.13 directly says what I'm doing is wrong.

I understand that 0 in a ptr context is NULL and that ANSI also allows
(void *)0 as an interpretation of NULL. I also understand that NULL for
an (int *) can be internally different to (char *) (Q5.17).

No, no, this is wrong. You must stop thinking of NULL as _being_
something. NULL is merely a macro; it has no real existence except as a
convenient identifier for something real. And in the paragraph above,
you confuse it with two, very different, real things.
On the one hand, you have a null pointer. This is an actual pointer
value, which exists during run time. It is a valid pointer, in that it
can be passed to functions and assigned to pointer objects, but it does
not point at any object; dereferencing it causes undefined behaviour. It
is this null pointer which can have different representations depending
on type.
Then there's a null pointer _constant_. It exists in your source code.
It can be either an integer constant with value 0, or such a constant
cast to void *. Of course, the most common null pointer constants you
find in real source code are 0 and (void *)0, but notoriously, '-'-'-'
would work just as well. Any null pointer constant serves just as well
for any type null pointer; int *ip=0; is as valid as float *fp=0;. You
use a null pointer constant in your source code whenever you want a null
pointer at run time, just as you use, say, '\n' whenever you want a
newline at run time.
And finally, there's NULL. NULL is a macro, defined in several Standard
headers; it is defined as a null pointer constant. Note: _a_ null
pointer constant. The Standard doesn't require it to be any kind of null
pointer constant, and you needn't care; all you need to know is that
NULL is some kind of null pointer constant, and can be used for clarity
wherever you would use a normal npc. But you don't _have_ to; you can
program in C without ever using NULL, if you prefer to use an
undisguised null pointer constant.
What I don't understand is why typecasting a NULL is not necessary.

For example:
a) int *p = NULL;
b) execl("/bin/sh", "sh", "-c", "date", NULL);

So (a) is OK, as the compiler can determine that NULL (if 0) should be
(int *)0.

It can determine the same thing, btw, if NULL is defined as (void *)0,
or even as ((void *)!sizeof(int)), but yes.
But for (b) I get the impression that it is incorrect and
should really be:

execl("/bin/sh", "sh", "-c", "date", (char *)NULL);

Yes, but that's because execl() is a special case. It is a variadic
function. You need to be much more careful with your parameters to
variadic functions than with normal functions. Since the compiler has
exactly zero information about the variadic arguments, it cannot know
that you meant a null pointer constant rather than an integer zero with
that 0. Even if you pass a (void *)0, it cannot figure out whether the
function needs an int * or a float *. Therefore, it needs to be told,
explicitly.
Three asides, here: 1. This is also true for the *printf()/*scanf()
family of functions, since they're variadic, as well; and obviously also
for any variadic functions you might define yourself. 2. Null pointer
constants are not the only thing affected; you must also be careful with
chars, shorts, and floats, since the default argument promotions are
performed on the variadic arguments (but not on the fixed ones!). 3. All
of this is true of functions with an old-style, non-prototype
declaration, or without a declaration at all, but ideally you shouldn't
have any of those around any more.

Summarising:
If you are not dealing with pointers, don't use NULL at all.
If you are dealing with the variable arguments of a variadic function,
and need to pass a literal null pointer, use either NULL cast to the
required type, or 0 cast to the required type. (You could, in principle,
also use (void *)0 cast to the required type, but that's overkill.)
Either works just as well as the other.
If you need a literal null pointer in any other pointer context, use
either 0, or (void *)0, or NULL, as you please. The compiler will detect
that you used a null pointer constant in a pointer context, and convert
it to the required type null pointer.

Richard
 
J

Jason Curl

Charlie said:
The source snippet is below. The question is:
- When I use calloc to allocate a block of memory, preinitialising it to
zero, is this equivalent (and portable C) to iterating through the
structure 128 times setting the individual elements to NULL (i.e. is
myFunc() and myFunc2() equivalent in functionality)?

struct _KeySequence {
KeyCallback *cb;
struct _KeySequence *next;
};

struct _KeySequence *node;

int myFunc2(void)
{
int j;

node = malloc(128 * sizeof(struct _KeySequence));
if (node == NULL) {
return 0;
} else {
for (j=0; j<127; j++) { <<<< off by one error
node[j].cb = NULL;
node[j].next = NULL;
}
return 1;
}
}


Hi Jason,

The question about equivalence of setting pointers to NULL vs: initializing the
array to all bits zero has been addressed thoroughly in other posts, but no one
mentioned another reason why the initialization loop is not equivalent in your
example to the calloc approach.

You don't loop over all the elements in the array, as you made an off by one
error :
for (j = 0; j < 127; j++) loops over 127 elements and leaves the last one
(at offset 127) untouched !

As a rule of thumb, you should never have to manipulate/compute off by one
numbers, nor use the <= operator.
Common C idioms make this unnecessary :

for (i = 0; i < n; i++) { ... } loops over n elements upwards
for (i = n; i-- > 0;) { ... } loops over n elements downwards
or possibly
for (int i = n; --i >= 0; ) { ... } but only works on signed i !

functions that take a buffer size should not be passed (sizeof(buf)-1), and
should be written as such.

Chqrlie.
Charlie, Thanks :) I feel kind of sheepish now seeing this silly
mistake. I knew I should have defined a macro called something and used
that instead to relate to the allocation of the buffer space. But I am
glad I follow your advice everywhere else in the code I've written!
 
J

Jason Curl

Richard said:
No, no, this is wrong. You must stop thinking of NULL as _being_
something. NULL is merely a macro; it has no real existence except as a
convenient identifier for something real. And in the paragraph above,
you confuse it with two, very different, real things.
On the one hand, you have a null pointer. This is an actual pointer
value, which exists during run time. It is a valid pointer, in that it
can be passed to functions and assigned to pointer objects, but it does
not point at any object; dereferencing it causes undefined behaviour. It
is this null pointer which can have different representations depending
on type.
Then there's a null pointer _constant_. It exists in your source code.
It can be either an integer constant with value 0, or such a constant
cast to void *. Of course, the most common null pointer constants you
find in real source code are 0 and (void *)0, but notoriously, '-'-'-'
would work just as well. Any null pointer constant serves just as well
for any type null pointer; int *ip=0; is as valid as float *fp=0;. You
use a null pointer constant in your source code whenever you want a null
pointer at run time, just as you use, say, '\n' whenever you want a
newline at run time.
And finally, there's NULL. NULL is a macro, defined in several Standard
headers; it is defined as a null pointer constant. Note: _a_ null
pointer constant. The Standard doesn't require it to be any kind of null
pointer constant, and you needn't care; all you need to know is that
NULL is some kind of null pointer constant, and can be used for clarity
wherever you would use a normal npc. But you don't _have_ to; you can
program in C without ever using NULL, if you prefer to use an
undisguised null pointer constant.




It can determine the same thing, btw, if NULL is defined as (void *)0,
or even as ((void *)!sizeof(int)), but yes.




Yes, but that's because execl() is a special case. It is a variadic
function. You need to be much more careful with your parameters to
variadic functions than with normal functions. Since the compiler has
exactly zero information about the variadic arguments, it cannot know
that you meant a null pointer constant rather than an integer zero with
that 0. Even if you pass a (void *)0, it cannot figure out whether the
function needs an int * or a float *. Therefore, it needs to be told,
explicitly.
Three asides, here: 1. This is also true for the *printf()/*scanf()
family of functions, since they're variadic, as well; and obviously also
for any variadic functions you might define yourself. 2. Null pointer
constants are not the only thing affected; you must also be careful with
chars, shorts, and floats, since the default argument promotions are
performed on the variadic arguments (but not on the fixed ones!). 3. All
of this is true of functions with an old-style, non-prototype
declaration, or without a declaration at all, but ideally you shouldn't
have any of those around any more.

Summarising:
If you are not dealing with pointers, don't use NULL at all.
If you are dealing with the variable arguments of a variadic function,
and need to pass a literal null pointer, use either NULL cast to the
required type, or 0 cast to the required type. (You could, in principle,
also use (void *)0 cast to the required type, but that's overkill.)
Either works just as well as the other.
If you need a literal null pointer in any other pointer context, use
either 0, or (void *)0, or NULL, as you please. The compiler will detect
that you used a null pointer constant in a pointer context, and convert
it to the required type null pointer.

Richard

Richard, what you've written (and most other people too) has really
clarified my understanding of NULL in this topic.

Thanks a lot!
 
T

Thomas Stegen

Jack said:
I am almost certain that you are misremembering or misunderstanding
the DJGPP FAQ. Most likely you are confusing the difference between
the macro NULL and the representation of a null pointer, which are
different but related things.

There seems to be widespread confusion. The confusions with the
difference between the macro NULL and a null pointer is probably a bit
deeper than just that.

So I would just like to add that the confusion is often about the value
which is a null pointer and the representation of this value. The reason
for this is probably (except for poor teachers) that one of the tokens
that can represent a null pointer in C source is 0.

So there are three things here.
- The value of a null pointer (which is the null pointer. Asking what
is the value of a null pointer is like asking what the value of the
integer 2 is).

- the C constructs that represents null pointers in the source (includes
NULL).

- The implementations internal representation of null pointers.

These are all orthogonal concepts and are hence not dependent of
eachother. Each can change without affecting the others. (Though I am
not sure if the first can change at all in any meaningful way...)

I should perhaps have qualified this a bit more, but I must dash.
 
C

Charlie Gordon

execl() isn't defined by C but taking this to be an example of the use of
NULL in a variable argument list then, yes, the cast is needed there,
because it is up to the caller code to ensure the argument value has the
correct type.

The prototype for this POSIX function is:

int execl(const char *, const char *, ...);

The compiler doesn't know that the execl() function expects only pointers to
chars as extra arguments.
There is no way to specify that in the C language.
This example is very important : it shows potential problems for architectures
where pointers may have different representations for different types (too bad
for those exotic and mostly archaic oddities), but it also shows a different
kind of problem for architectures where pointers and ints have a different size.

Consider a 64 bit ABI with 32 bit ints, 64 bit longs, 64 bit pointers and
size_t, but a stack alignment of 4 bytes.
NULL should be defined as ((void*)0) on such an architecture, instead of just 0.
Otherwise calling execl this way will cause problems :

execl("/bin/sh", "sh", "-c", "date", NULL); // not a very useful way to die ;-)

because NULL in this context would expand to 0 and be passed as the integer 0

and I haven't seen many programmers casting NULL to (char*) in such a context.

Chqrlie.
 
J

Jason Curl

Charlie said:
The prototype for this POSIX function is:

int execl(const char *, const char *, ...);

The compiler doesn't know that the execl() function expects only pointers to
chars as extra arguments.
There is no way to specify that in the C language.
This example is very important : it shows potential problems for architectures
where pointers may have different representations for different types (too bad
for those exotic and mostly archaic oddities), but it also shows a different
kind of problem for architectures where pointers and ints have a different size.

Consider a 64 bit ABI with 32 bit ints, 64 bit longs, 64 bit pointers and
size_t, but a stack alignment of 4 bytes.
NULL should be defined as ((void*)0) on such an architecture, instead of just 0.
Otherwise calling execl this way will cause problems :

execl("/bin/sh", "sh", "-c", "date", NULL); // not a very useful way to die ;-)

because NULL in this context would expand to 0 and be passed as the integer 0

and I haven't seen many programmers casting NULL to (char*) in such a context.

From what I gather reading the C FAQ is the best way is to typecast
NULL to (char*) as it could be such a machine where an (int*) has a
different representation to (char*). The compiler wouldn't know what to
cast a (void*) to. I guess it works because on most machines (void*) has
the same representation internally as (int*) and (char*).

It appears from everything I've read so far that just using NULL is not
portable, as it was my original intention of a slight optimisation of
using calloc instead of malloc and setting the pointers to NULL explicitly.
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,154
Messages
2,570,870
Members
47,400
Latest member
FloridaFvt

Latest Threads

Top