printf("%p\n", (void *)0);

C

CBFalconer

Andrey said:
.... snip ...

This has already been covered above. Once again, the standard says
that null pointer arguments can be specifically allowed by the
specification of the concrete function from the standard library.
'free' function is a good example of such function: its
specification explicitly states that passing a null pointer to it
is OK (does noting). The specification of 'printf' does not say
that passing null pointers to it is OK. That's exactly what
constitutes the problem.

However the standard does say that dereferencing NULL is bad, and
that is exactly what is required for all usages of NULL in printf
except the %p descriptor. It is that omission that is giving some
people qualms.
 
K

Keith Thompson

Quentarez said:
I, personally, would not consider this to be UB.

I don't have supreme knowledge of the standard, nor do I have any
references to the standard to support this claim.

I decided to take a different route, and see what the compiler does with a
NULL pointer. I tested it on two compilers: MSVC6 and gcc 3.3.5.

Observing the behavior of any number of implementations doesn't
demonstrate that the behavior is defined. Undefined behavior is
exactly that, undefined. Specifically, it's

behavior, upon use of a nonportable or erroneous program construct
or of erroneous data, for which this International Standard
imposes no requirements

(C99 3.4.3p1)

Even assuming that it's UB, the behaviors of MSVC6 and gcc are
entirely consistent with the standard. (*Any* behavior would be
entirely consistent with the standard.)

The only way to determine whether a given construct invokes undefined
behavior is by a careful reading of the standard.
It has already been shown previous to this post that (void *)0 is indeed a
way to get a NULL pointer, so I will refer to it as a NULL pointer from now
on.

It's better to refer to this as a "null pointer", not a "NULL
pointer". NULL is a macro that expands to a null pointer constant;
using all-caps NULL for anything else is misleading.
From what I've learned, a pointer always points /somewhere/, even if that
somewhere is not where you intended.

Not necessarily. As far as the language is concerned a null pointer
points nowhere. (It might happen to point to some location in
physical or virtual memory, but no program can determine that without
invoking undefined behavior.)

More concretely, on a system with 128 megabytes of virtual memory, a
pointer with the value 0x10000000 (that's 256 megabytes) doesn't point
anywhere -- or maybe it points to a memory card you haven't installed
yet. (For the sake of this example, I'm making several assumptions
about pointers that are commonly true, but are not required by the
standard.)
From simply compiling and running the given code, I get the output 00000000
on MSVC6 and (nil) on gcc.

Using gcc on three different platforms, I get "0x0", "0", and "(nil)".
It's the runtime library, not the compiler, that implements printf.
(As far as the standard is concerned, the compiler and the library are
both part of the implementation; in other words, "gcc" is not a
complete description of the implementation.)

[snip]
Based on my observations, it seems that

printf("%p\n", (void *)0);

is not UB.

Based on your observations, it could very well be UB.
 
K

Keith Thompson

CBFalconer said:
However the standard does say that dereferencing NULL is bad, and
that is exactly what is required for all usages of NULL in printf
except the %p descriptor. It is that omission that is giving some
people qualms.

For that matter, a printf implementation *could* attempt to
dereference the pointer associated with the "%p" descriptor (after an
appropriate conversion, of course). It might be useful for the "%p"
output to show both the value of the pointer and, say, the first few
bytes of the memory it points to. "The value of the pointer is
converted to a sequence of printing characters, in an
implementation-defined manner."

In my opinion such an implementation that blew up attempting to
dereference a null pointer would be less than useful, and would
violate what I presume to be the intent of the standard, but the
actual wording of the standard doesn't quite forbid it.
 
Q

Quentarez

Quentarez said:
I, personally, would not consider this to be UB.

I don't have supreme knowledge of the standard, nor do I have any
references to the standard to support this claim.

I decided to take a different route, and see what the compiler does with a
NULL pointer. I tested it on two compilers: MSVC6 and gcc 3.3.5.

Observing the behavior of any number of implementations doesn't
demonstrate that the behavior is defined. Undefined behavior is
exactly that, undefined. Specifically, it's

behavior, upon use of a nonportable or erroneous program construct
or of erroneous data, for which this International Standard
imposes no requirements

(C99 3.4.3p1)

Even assuming that it's UB, the behaviors of MSVC6 and gcc are
entirely consistent with the standard. (*Any* behavior would be
entirely consistent with the standard.)

The only way to determine whether a given construct invokes undefined
behavior is by a careful reading of the standard.
It has already been shown previous to this post that (void *)0 is indeed a
way to get a NULL pointer, so I will refer to it as a NULL pointer from now
on.

It's better to refer to this as a "null pointer", not a "NULL
pointer". NULL is a macro that expands to a null pointer constant;
using all-caps NULL for anything else is misleading.
From what I've learned, a pointer always points /somewhere/, even if that
somewhere is not where you intended.

Not necessarily. As far as the language is concerned a null pointer
points nowhere. (It might happen to point to some location in
physical or virtual memory, but no program can determine that without
invoking undefined behavior.)

More concretely, on a system with 128 megabytes of virtual memory, a
pointer with the value 0x10000000 (that's 256 megabytes) doesn't point
anywhere -- or maybe it points to a memory card you haven't installed
yet. (For the sake of this example, I'm making several assumptions
about pointers that are commonly true, but are not required by the
standard.)
From simply compiling and running the given code, I get the output 00000000
on MSVC6 and (nil) on gcc.

Using gcc on three different platforms, I get "0x0", "0", and "(nil)".
It's the runtime library, not the compiler, that implements printf.
(As far as the standard is concerned, the compiler and the library are
both part of the implementation; in other words, "gcc" is not a
complete description of the implementation.)

[snip]
Based on my observations, it seems that

printf("%p\n", (void *)0);

is not UB.

Based on your observations, it could very well be UB.

Ah! My definition of UB was completely incorrect. Thank you very much for
your response and clarification! :)
 
A

aegis

Keith said:
Observing the behavior of any number of implementations doesn't
demonstrate that the behavior is defined. Undefined behavior is
exactly that, undefined. Specifically, it's

behavior, upon use of a nonportable or erroneous program construct
or of erroneous data, for which this International Standard
imposes no requirements

(C99 3.4.3p1)

Even assuming that it's UB, the behaviors of MSVC6 and gcc are
entirely consistent with the standard. (*Any* behavior would be
entirely consistent with the standard.)

The only way to determine whether a given construct invokes undefined
behavior is by a careful reading of the standard.


It's better to refer to this as a "null pointer", not a "NULL
pointer". NULL is a macro that expands to a null pointer constant;
using all-caps NULL for anything else is misleading.


Not necessarily. As far as the language is concerned a null pointer
points nowhere. (It might happen to point to some location in
physical or virtual memory, but no program can determine that without
invoking undefined behavior.)

It does point somewhere. It points to no object. And 'no object' does
not
mean 'nowhere'.
 
N

Niklas Matthies

For that matter, a printf implementation *could* attempt to
dereference the pointer associated with the "%p" descriptor (after an
appropriate conversion, of course). It might be useful for the "%p"
output to show both the value of the pointer and, say, the first few
bytes of the memory it points to. "The value of the pointer is
converted to a sequence of printing characters, in an
implementation-defined manner."

The first few bytes of memory it points to is not part of the value
of the pointer, though (well, unless it points to itself...).
Arguably, converting "the value of the pointer" should never require
dereferencing the value.

-- Niklas Matthies
 
K

Keith Thompson

Niklas Matthies said:
The first few bytes of memory it points to is not part of the value
of the pointer, though (well, unless it points to itself...).
Arguably, converting "the value of the pointer" should never require
dereferencing the value.

That's a good point. My thought is that the phrase "in an
implementation-defined manner" might leave enough leeway to look at
what it points to (and it could actually be useful). But since it's a
void*, it's hard to decide how many bytes to display.
 
P

Peter Nilsson

And printing an integer could cause that integer to be converted
to a pointer and subsequently dereferenced. The standard doesn't
seem to preclude it, so it must be an option, right? ;)

Your use of the word 'both' implies you already recognise the
distinction.
That's a good point. My thought is that the phrase "in an
implementation-defined manner" might leave enough leeway to
look at what it points to...

Think about these...

char a[42];
printf("%p\n", a + 42);

char *p = malloc(0);
printf("%p\n", p);

More pragmatically, consider a conforming implementation
that allows pointers to be generated for all sorts of things,
including memory mapped I/O. Such an implementation would have
to check the pointer _very carefully_ to determine that
subsequent dereferencing will not reformat the harddisk!
 
B

Ben Pfaff

Keith Thompson said:
For that matter, a printf implementation *could* attempt to
dereference the pointer associated with the "%p" descriptor (after an
appropriate conversion, of course). It might be useful for the "%p"
output to show both the value of the pointer and, say, the first few
bytes of the memory it points to.

That would make the following impossible to implement portably
without invoking undefined behavior, whereas I don't think it
should be:
printf("%p\n", malloc(0));
A real implementation would probably have extra knowledge though.
 
K

Keith Thompson

aegis said:
It does point somewhere. It points to no object. And 'no object'
does not mean 'nowhere'.

Yes, it does. There isn't some mythical object called "no object".
"It points to no object" means the same as "It doesn't point to an
object".

To use a physical analogy, suppose I have a laser pointer that I can
use to point to various objects. If I aim it at an empty spot in the
sky, it's still pointing, but it's not pointing to any object. If I
turn it off, it's not pointing.

A null pointer is like a laser pointer that's turned off.

If you view it as a raw machine-level address (physical or virtual,
whichever is used for C pointers), a null pointer *might* happen to be
the address of some actual chunk of memory, or it might contain a
virtual address that's not mapped into the current process's address
space or a physical address that doesn't correspond to any real
memory. The C standard doesn't distinguish between these cases; as
far as C is concerned, a null pointer doesn't point. No program can
determine whether a null pointer acts as if it pointed to something
without invoking undefined behavior.

What the C standard says is that it "is guaranteed to compare unequal
to a pointer to any object or function"; it also says that applying
the unary "*" operator to an invalid pointer (including a null
pointer) invokes undefined behavior.
 
A

aegis

Keith said:
Yes, it does. There isn't some mythical object called "no object".
"It points to no object" means the same as "It doesn't point to an
object".

Noone said it is mythical. The standard clearly states that
the purpose of a null pointer is to distinguish between
pointing to an object and no object. You can think of it as having
a set of objects with one object for representing no object.
Let's call the special object the sentinel.
When our pointer points to the sentinel, it means 'no object'.
When our pointer is not pointing to the sentinel, it is pointing
to an object(except where the value is invalid). In either case,
our pointer points to something but is never 'turned off' since
it is always functioning. That is the point of pointers, you see?
To point to things. If the pointer were to be 'turned off' it would
stop functioning, in which case we would be unable to determine
if it points to the object that represents no object. ;-)
 
P

Peter Nilsson

aegis said:
Noone said it is mythical. The standard clearly states that
the purpose of a null pointer is to distinguish between
pointing to an object and no object.

What it clearly says is "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."

Keith's point is that there is no requirement that a null pointer
actually point to a valid address location.

Consider a cpu with 24-bit address space, but 32-bit address/data
registers. Such a machine may have 255 or more null pointer
representations, none of which point to a genuine addressable
memory location.
You can think of it as having a set of objects with one object
for representing no object.

That is one possible method, and indeed it is the most common
one. Address 0 (all bits zero), is typically used since it either
points to unused low memory, or possibly header stuff at the
beginning of a text/data/etc... segment.
Let's call the special object the sentinel.

You're talking in terms of objects. This is misleading since the
standard clearly defines what is and what isn't an object. It
makes no sense within the C context to describe null pointers
as pointing to an object, even if that object is never 'used'.

The only thing the standard requires is that there is at least
one null pointer _representation_. Whatever that (or those)
representations are is not specified by the C standards. They
are not required to be valid physical or virtual addresses.
They may even be technical 'trap' representations within the
machine, i.e. addresses which, if the contents are fetched,
will raise a bus interupt, but which can otherwise be compared
safely.
 
A

aegis

Peter said:
What it clearly says is "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."

Keith's point is that there is no requirement that a null pointer
actually point to a valid address location.

That isn't even being disputed. And if that is what he
was trying to get across then he is horribly confused.

I just pointed out that saying pointing to 'no object' is not
the same as pointing 'nowhere'.
Consider a cpu with 24-bit address space, but 32-bit address/data
registers. Such a machine may have 255 or more null pointer
representations, none of which point to a genuine addressable
memory location.

I don't see how this is relevant.
That is one possible method, and indeed it is the most common
one. Address 0 (all bits zero), is typically used since it either
points to unused low memory, or possibly header stuff at the
beginning of a text/data/etc... segment.

I don't see why you are introducing these irrelevant
details. Again, I was pointing out that pointing to
'no object' does not mean 'pointing to nowhere'.
You're talking in terms of objects. This is misleading since the
standard clearly defines what is and what isn't an object. It
makes no sense within the C context to describe null pointers
as pointing to an object, even if that object is never 'used'.

The point of that was to aid in distinguishing the object
that represents 'no object'.

And I did so in an abstract way to show that thinking
a null pointer points to 'nowhere' is nonsense.
 
P

pete

Keith said:
For that matter, a printf implementation *could* attempt to
dereference the pointer associated with the "%p" descriptor (after an
appropriate conversion, of course). It might be useful for the "%p"
output to show both the value of the pointer and, say, the first few
bytes of the memory it points to. "The value of the pointer is
converted to a sequence of printing characters, in an
implementation-defined manner."

In my opinion such an implementation that blew up attempting to
dereference a null pointer would be less than useful, and would
violate what I presume to be the intent of the standard, but the
actual wording of the standard doesn't quite forbid it.

How about printing the value of the one past pointer,
do you think that is undefined?

/* BEGIN new.c */

#include <stdio.h>

int main(void)
{
unsigned char a;

printf("&a + 1 is %p.\n", (void *)(&a + 1));
return 0;
}

/* END new.c */
 
I

infobahn

aegis said:
The point of that was to aid in distinguishing the object
that represents 'no object'.

But there isn't an object that represents 'no object'. If there were
such an object, a null pointer would not be allowed to point to it,
since null pointers don't point to any object, not even a 'no object'
object.
 
A

aegis

infobahn said:
But there isn't an object that represents 'no object'. If there were
such an object, a null pointer would not be allowed to point to it,
since null pointers don't point to any object, not even a 'no object'
object.

You are confusing things here. The set of objects
is an abstract notion. Don't confuse it with something
conrete. That being said, don't think of the 'object'
that represents no object, as a region of storage in
the execution environment. I thought that was obvious
given that I was talking in terms of sets. You could
subtitute the use of 'object' there with 'entity' and
produce the same meaning. This would then read as:

the 'entity' that represents 'no object'.

Which, in concrete terms, is what a null pointer does.

A pointer being the 'entity' and the value being the
thing that aids in making the distinction between 'object'
and 'no object'. By now, you should see that an 'entity'
points to either of the two(indeterminancy being irrelevant
to my point), but never 'nowhere'.
 
I

infobahn

aegis said:
You are confusing things here.

Obviously, I disagree.
The set of objects
is an abstract notion. Don't confuse it with something
conrete.

I must disagree again.
That being said, don't think of the 'object'
that represents no object, as a region of storage in
the execution environment.

If it isn't a region of storage in the execution environment, it
can't be an object, because that's what an object *is*.
I thought that was obvious
given that I was talking in terms of sets. You could
subtitute the use of 'object' there with 'entity' and
produce the same meaning. This would then read as:

the 'entity' that represents 'no object'.

I see no justification in the Standard for assuming the existence
of such an entity. The closest I can get to it is "null pointer",
which doesn't point to any object at all.
Which, in concrete terms, is what a null pointer does.

A pointer being the 'entity' and the value being the
thing that aids in making the distinction between 'object'
and 'no object'. By now, you should see that an 'entity'
points to either of the two(indeterminancy being irrelevant
to my point), but never 'nowhere'.

This 'no-object entity' doesn't exist. It isn't anywhere. And
therefore it's nowhere. So if a null pointer points to it,
that null pointer points nowhere. And if it doesn't, the point
is moot.
 
K

Keith Thompson

Ben Pfaff said:
That would make the following impossible to implement portably
without invoking undefined behavior, whereas I don't think it
should be:
printf("%p\n", malloc(0));

Much of the C library is impossible to implement portably without
invoking undefined behavior.
A real implementation would probably have extra knowledge though.

True. In any case, it was just an idle thought, probably not worth
spending too much time on.

I was trying to think of a reason why printf("%p", ptr) might invoke
undefined behavior if ptr is null -- but of course any non-stupid
implementer would check whether ptr==NULL before trying to print what,
if anything, it points to.
 
A

aegis

infobahn said:
Obviously, I disagree.


I must disagree again.


If it isn't a region of storage in the execution environment, it
can't be an object, because that's what an object *is*.

'no object' is a thing, and as a thing is an object.
Like a car is an object. A bicycle is an object.
A plane is an object. Etc. See the abstraction?

I see no justification in the Standard for assuming the existence
of such an entity. The closest I can get to it is "null pointer",
which doesn't point to any object at all.

That's what I was pointing out. It points to a thing.
That thing is called 'no object'. 'no object' isn't the
same as 'nowhere'. For if a pointer were to point
to 'nowhere', we would not be able to use it in any
meaningful way(you can think of 'nowhere' as a pointer
having an indeterminate value)
This 'no-object entity' doesn't exist. It isn't anywhere. And
therefore it's nowhere. So if a null pointer points to it,
that null pointer points nowhere. And if it doesn't, the point
is moot.

The 'no object' is somewhere. It is at NULL. :)
 
K

Keith Thompson

pete said:
Keith Thompson wrote: [...]
For that matter, a printf implementation *could* attempt to
dereference the pointer associated with the "%p" descriptor (after an
appropriate conversion, of course). It might be useful for the "%p"
output to show both the value of the pointer and, say, the first few
bytes of the memory it points to. "The value of the pointer is
converted to a sequence of printing characters, in an
implementation-defined manner."

In my opinion such an implementation that blew up attempting to
dereference a null pointer would be less than useful, and would
violate what I presume to be the intent of the standard, but the
actual wording of the standard doesn't quite forbid it.

How about printing the value of the one past pointer,
do you think that is undefined?

/* BEGIN new.c */

#include <stdio.h>

int main(void)
{
unsigned char a;

printf("&a + 1 is %p.\n", (void *)(&a + 1));
return 0;
}

/* END new.c */

That's another thing that needs to be resolved. In my opinion, the
answer *should* be no; it should be possible to pass any pointer to an
object, or just past the end of an object, or a null pointer, to
printf("%p", ...) without invoking undefined behavior.

The standard tells us that a call to a library function invokes
invokes undefined behavior if an argument has "an invalid value". For
pointers, though, validity depends on the context in which it's used;
a pointer that's valid for assignment or equality comparison may not
be valid for pointer arithmetic, a pointer that's valid for arithmetic
may not be valid for dereferencing, and a pointer that's valid as a
pointer to one type may be invalid as a pointer to another type (after
conversion). The standard needs to be clearer about which pointer
values are "valid" for printf("%p", ...).

It might even be nice to have a more expansive definition of validity
for this context than for others. For example, the standard *could*
specify that this:

void *ptr = malloc(1);
if (ptr != NULL) {
free(ptr);
printf("ptr = %p\n", ptr);
}

does not invoke undefined behavior. (printf doesn't have to treat the
value as a pointer; it could, for example, type-pun it as an array of
unsigned char to determine a hexadecimal representation.)

I don't *think* I'm seriously suggesting this. (I wonder whether it
would break any existing implementations.)
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,164
Messages
2,570,901
Members
47,439
Latest member
elif2sghost

Latest Threads

Top