By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,143 Members | 1,254 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,143 IT Pros & Developers. It's quick & easy.

Definition of NULL

P: n/a
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;
}
}
Nov 14 '05 #1
Share this Question
Share on Google+
29 Replies


P: n/a
Jason Curl wrote:
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.
Nov 14 '05 #2

P: n/a
Jason Curl <j_****************************@foo.bar> writes:
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.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #3

P: n/a
Jason Curl <j_****************************@foo.bar> wrote:
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
Nov 14 '05 #4

P: n/a
go***********@burditt.org (Gordon Burditt) writes:
[...]
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.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #5

P: n/a

"Jason Curl" <j_****************************@foo.bar> wrote
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.
Nov 14 '05 #6

P: n/a
On Tue, 16 Nov 2004 19:02:04 GMT, Tatu Portin <ax****@mbnet.fi> wrote
in comp.lang.c:
Jason Curl wrote:
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'.


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.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #7

P: n/a
Jason Curl wrote:

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

--
pete
Nov 14 '05 #8

P: n/a
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.
--
pete
Nov 14 '05 #9

P: n/a
"Jason Curl" <j_****************************@foo.bar> wrote in message
news:cn**********@newshost.mot.com...
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.
Nov 14 '05 #10

P: n/a
Keith Thompson wrote:
Jason Curl <j_****************************@foo.bar> writes:
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?
Nov 14 '05 #11

P: n/a


Jason Curl wrote:
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
--
E-Mail: Mine is a gmx dot de address.

Nov 14 '05 #12

P: n/a
On Tue, 16 Nov 2004 19:31:42 +0000, Keith Thompson wrote:
Jason Curl <j_****************************@foo.bar> writes:
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,


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".
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 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
Nov 14 '05 #13

P: n/a
On Wed, 17 Nov 2004 10:18:59 +0100, Jason Curl wrote:

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


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

Nov 14 '05 #14

P: n/a
Jason Curl <j_****************************@foo.bar> wrote:
Keith Thompson wrote:
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).


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
Nov 14 '05 #15

P: n/a
Charlie Gordon wrote:
"Jason Curl" <j_****************************@foo.bar> wrote in message
news:cn**********@newshost.mot.com...

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!
Nov 14 '05 #16

P: n/a
Richard Bos wrote:
Jason Curl <j_****************************@foo.bar> wrote:

Keith Thompson wrote:
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).

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


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

Thanks a lot!
Nov 14 '05 #17

P: n/a
Jack Klein wrote:
On Tue, 16 Nov 2004 19:02:04 GMT, Tatu Portin <ax****@mbnet.fi> wrote
in comp.lang.c:
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.


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.

--
Thomas.
Nov 14 '05 #18

P: n/a
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.


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.
Nov 14 '05 #19

P: n/a
Charlie Gordon wrote:
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.

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.

Chqrlie.

Nov 14 '05 #20

P: n/a
Jason Curl wrote:
Charlie Gordon wrote:
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.


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.


Sorry - Clarification - just using NULL in this context for variadic
functions is not portable. It's also not portable to assume that an
internal representation for a null pointer is zero.

Chqrlie.


Sorry I didn't read my response the first time!
Nov 14 '05 #21

P: n/a
Michael Mair <Mi**********@invalid.invalid> wrote:
Jason Curl wrote:
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.


The compiler does not know the parameter type if the function
is variadic. For variadic functions you must cast the parameters
if their type is not what the function is expecting.

The OP did not bother to say what the prototype
for "execl" was. If we suppose for the moment that he meant:

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

and that the function expects the extra arguments to be
of type "char *", then it would be undefined behaviour
to pass a bare NULL like he did in the first example.
Nov 14 '05 #22

P: n/a
Old Wolf wrote:
Michael Mair <Mi**********@invalid.invalid> wrote:
Jason Curl wrote:
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.

The compiler does not know the parameter type if the function
is variadic. For variadic functions you must cast the parameters
if their type is not what the function is expecting.

The OP did not bother to say what the prototype
for "execl" was. If we suppose for the moment that he meant:

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

and that the function expects the extra arguments to be
of type "char *", then it would be undefined behaviour
to pass a bare NULL like he did in the first example.


Thanks for pointing that out.
I gathered from the rest of the discussion that execl() is supposed
to be variadic, too. This was completely unclear to me(*) which is
why I only considered the case that there was no prototype as
possible special case where I was not sure. You snipped that part
but essentially I would just use the cast if I had only a declaration
like
int execl();
but were sure about the number and types of arguments.
Cheers
Michael
____
(*) And that is a good example why people should at least have
the decency to provide prototypes for nonstandard functions they
are using.
--
E-Mail: Mine is an /at/ gmx /dot/ de address.
Nov 14 '05 #23

P: n/a
Jason Curl wrote:
I guess it works because on most machines (void*) has
the same representation internally as (int*) and (char*).


The representation of void* and char* is the same.

N869
6.2.5 Types
[#27] A pointer to void shall have the same representation
and alignment requirements as a pointer to a character type.

--
pete
Nov 14 '05 #24

P: n/a
"Jason Curl" <j_****************************@foo.bar> wrote in message
news:cn**********@newshost.mot.com...
Charlie Gordon wrote:
... 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*).


I Agree. But in this case, this is not the issue as char* and void* are supposed
to have the same representation.
The problem is with environments that define NULL this way :
#define NULL 0

This is needed for C++ because of their dubious choice to disallow implicit cast
of void* to other pointer types.

For C, it is not needed. It is correct but IMHO misleading :

It doesn't allow the compiler to catch blatant misunderstandings :

char buf[10];
buf[0] = NULL;

time_t tv = NULL;

double epsilon = NULL;

All these would get caught if NULL was (void*)0.
They will on a different architecture, so that ugly code is non portable anyway.

As per my initial example, having to cast NULL to whatever pointer type is
passed to a varadic function is needed, but a common mistake. And in the
following example :

printf("%p", NULL);

having to cast NULL to (void*) would definitely seem overkill, even though it
would be required to prevent bogus output on a the 64 bit ABI detailed above.

Conclusion : it is safer to define NULL to ((void*)0)

Chqrlie.
Nov 14 '05 #25

P: n/a
Michael Mair wrote:
Old Wolf wrote:
Michael Mair <Mi**********@invalid.invalid> wrote:
Jason Curl wrote:

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.


The compiler does not know the parameter type if the function
is variadic. For variadic functions you must cast the parameters
if their type is not what the function is expecting.

The OP did not bother to say what the prototype
for "execl" was. If we suppose for the moment that he meant:

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

and that the function expects the extra arguments to be
of type "char *", then it would be undefined behaviour
to pass a bare NULL like he did in the first example.

Thanks for pointing that out.
I gathered from the rest of the discussion that execl() is supposed
to be variadic, too. This was completely unclear to me(*) which is
why I only considered the case that there was no prototype as
possible special case where I was not sure. You snipped that part
but essentially I would just use the cast if I had only a declaration
like
int execl();
but were sure about the number and types of arguments.
Cheers
Michael
____
(*) And that is a good example why people should at least have
the decency to provide prototypes for nonstandard functions they
are using.


Sorry if it wasn't clear about the prototype. But I used this because
the C FAQ used this, and I was asking for clarification on my second
post about the C FAQ. I had really thought that the original post made
it clear I was talking about an example in the C FAQ:

"execl("/bin/sh", "sh", "-c", "date", (char *)NULL);
or is this wrong too, and it should be as in Q5.2?"
Nov 14 '05 #26

P: n/a
pete wrote:
Jason Curl wrote:

I guess it works because on most machines (void*) has
the same representation internally as (int*) and (char*).

The representation of void* and char* is the same.

N869
6.2.5 Types
[#27] A pointer to void shall have the same representation
and alignment requirements as a pointer to a character type.

Thanks Pete. This opens up a new interesting set of conundrums for myself.

Let's imagine we have a function signature:

void CallBack(void *data);

I use often in code this type of datastructure, where I might say:

typedef void (cb)(void *data);

int RegisterCallBack(cb *fn, void *data);

int result;
struct _myData {
int foo;
char bar;
} myData;

int main(void) {
result = RegisterCallBack(CallBack, &myData);

return MainEventLoop();
}

So that, when I call a function in a loop that could for example monitor
external inputs (keyboard, a hardware switch, it doesn't matter), when a
condition is met my function 'CallBack()' is called. CallBack() would of
course typecast (_myData*)data.

How does the standard deal with the case when sizeof(_myData *) is
smaller than sizeof(void*) (or even indeed if the internal
representations might be different)?

I can only imagine that void* is 'typeless' so it is automatically
promoted to _myData* with any conversions required. Indeed, it might be
an int* instead of _myData*.

This then implies to me that void* must be able to accomodate all types
of pointers
Nov 14 '05 #27

P: n/a
On Thu, 18 Nov 2004 15:57:23 +0100, Jason Curl wrote:

....
Let's imagine we have a function signature:

void CallBack(void *data);

I use often in code this type of datastructure, where I might say:

typedef void (cb)(void *data);

int RegisterCallBack(cb *fn, void *data);

int result;
struct _myData {
Avoid defining identifiers starting with underscores. There are
situation where this is OK, but it is FAR easier not to have to worry
about it than to remember all the rules.
int foo;
char bar;
} myData;

int main(void) {
result = RegisterCallBack(CallBack, &myData);

return MainEventLoop();
}

So that, when I call a function in a loop that could for example monitor
external inputs (keyboard, a hardware switch, it doesn't matter), when a
condition is met my function 'CallBack()' is called. CallBack() would of
course typecast (_myData*)data.
Since C will convert implicitly from void * to other
non-function pointer types, a cast is not explicitly required and should
probably be avoided.
How does the standard deal with the case when sizeof(_myData *) is
smaller than sizeof(void*) (or even indeed if the internal
representations might be different)?
C says you can convert from a valid pointer to a data type to void * and
back again and you will get the original value back. Aldo the void * value
will be a pointer to the first byte of the object pointed to, or null if
the original pointer was null.

So if the 2 pointer types have different representations the compiler must
make the appropriate representation changes. This is perfectly reasonable
and normal concept when converting a value to a different type in C.
I can only imagine that void* is 'typeless'
The pointer has a perfectly well-defined type and representation (as much
as the representation of any pointer is defined).

so it is automatically promoted to _myData* with any conversions required. Indeed, it might be
an int* instead of _myData*.
Pointers aren't automatically (*) "promoted", they are converted from one
type to another as the source code requires.

(*) there are some circumstances where you could argue this e.g.

int *ip = ...;
void *vp = ...;

ip == vp /* the value of ip is converted to void * and compared to vp */

cond ? ip : vp; /* the result is either ip converted to void * or vp */

The conversion has to be this way because every valid int * can be
converted to a void * but the reverse is not guaranteed.
This then implies to me that void* must be able to accomodate all types
of pointers


In the sense of being able to convert to void * and back again, yes,
except for pointers to functions for which there are no such guarantees.

Lawrence

Nov 14 '05 #28

P: n/a
pete <pf*****@mindspring.com> writes:
Jason Curl wrote:
I guess it works because on most machines (void*) has
the same representation internally as (int*) and (char*).


The representation of void* and char* is the same.


Yes, but that doesn't help in the example we're discussing. The
execl() function (which is defined by POSIX but not by the C standard)
takes a variable number of arguments. If you pass NULL as one of the
variable arguments, the compiler has no way to know what type the
function is expecting (i.e., how the function is going to call
va_arg()). As it happens, the function is expecting the argument to
be of type char*. If NULL is defined as (void*)0, you're probably ok,
since void* and char* have the same representation. But if NULL is
defined as 0 (which is an equally valid definition), the argument is
going to be passed as an int.

Many systems will fail to catch this error, either because NULL is
defined as (void*)0, or because int and char* happen to be the same
size *and* a null char* is represented as all-bits-zero *and* variable
arguments of types int and char* are passed by the same mechanism.
Break any one of these last three assumptions, and it's likely to blow
up in your face.

Of course, it's much easier and safer to add the cast than to figure
out whether you can get away with omitting it.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #29

P: n/a
Groovy hepcat Jason Curl was jivin' on Tue, 16 Nov 2004 18:47:59 +0100
in comp.lang.c.
Definition of NULL's a cool scene! Dig it!
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).
It's really very simple. You're probably confusing three things,
which is a very common error.

1) A null pointer is a pointer guaranteed not to point at an object or
function. The actual representation of a null pointer is not
specified.

2) A null pointer constant is a constant of type int with a value of 0
or such a constant cast to void *. In a pointer context it is
converted to a null pointer.

3) NULL is a macro (defined in various standard C headers) that
evaluates to a null pointer constant.
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, not quite. The problem is that calloc() sets the memory it
allocates to "all bits zero". But a null pointer is not necesssarily
"all bits zero". Assigning a null pointer constant (which has the
value 0) to a pointer is guaranteed to make it a null pointer; but
this does not necessarily mean "all bits zero". As stated in point 1)
above, the representation is not specified.
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;
}
}


myFunc2() sets all the pointers in the array to null pointers.
myFunc(), however, may or may not, depending on how null pointers are
represented in the implementation. Technically the value of these
pointers is, therefore, indeterminate.

--

Dig the even newer still, yet more improved, sig!

http://alphalink.com.au/~phaywood/
"Ain't I'm a dog?" - Ronny Self, Ain't I'm a Dog, written by G. Sherry & W. Walker.
I know it's not "technically correct" English; but since when was rock & roll "technically correct"?
Nov 14 '05 #30

This discussion thread is closed

Replies have been disabled for this discussion.