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

K

Keith Thompson

Peter Nilsson said:
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.

Actually it could have 4278190080 such pointer representations
(2**32 - 2**24). (Whether they all be treated as C null pointers is
another question.)
 
K

Keith Thompson

aegis said:
Peter Nilsson wrote: [...]
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.

My only confusion is about what you're trying to say.
I just pointed out that saying pointing to 'no object' is not
the same as pointing 'nowhere'.

Then what's the difference? Assuming there's no valid address
location associated with a null pointer, in what sense does a null
pointer "point" anywhere?
 
I

infobahn

aegis said:
'no object' is a thing,

Really? I'd have thought it was 'no thing'.
and as a thing is an object.

No, in C an object is not a "thing", but a region of storage in
the execution environment.
Like a car is an object. A bicycle is an object.
A plane is an object. Etc. See the abstraction?

The abstraction is irrelevant, because the C Standard (which is what
we're on about) doesn't consider cars, bicycles, or planes to be
objects unless they are regions of storage in the execution
environment.
That's what I was pointing out. It points to a thing.

The C Standard doesn't agree with you.
That thing is called 'no object'.

Chapter and verse, please.
The 'no object' is somewhere. It is at NULL. :)

If, as you claim, the 'no object' is in fact an object, then
NULL can't point there.

"An integral 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 assigned to or compared for equality to a
pointer, the constant is converted to a pointer of that type. Such a
pointer, called a null pointer, is guaranteed to compare unequal to a
pointer to any object or function."
 
A

aegis

Keith said:
aegis said:
Peter Nilsson wrote: [...]
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.

My only confusion is about what you're trying to say.
I just pointed out that saying pointing to 'no object' is not
the same as pointing 'nowhere'.

Then what's the difference? Assuming there's no valid address
location associated with a null pointer, in what sense does a null
pointer "point" anywhere?

In terms of indeterminant values, it can't be said to point anywhere
meaningful. But given that a pointer object contains NULL as a value,
it is said to 'point' to 'no object'. Which is how we distinguish
between valid objects and no objects. 0, (void *)0, NULL, etc,
represent this by a distinguishable value. What is at this address
is 'no object'. See how meaningful it is? Saying a pointer
that is a null pointer, points to nowhere, is not meaningful.
And in fact, that is an oxymoron. Because as a pointer, it will point
to something. That is the nature of a pointer. But the issue is
whether or not that something it points at, is meaningful. And yes,
Keith, 'no object' is a thing. It is a distinguishable entity crafted
by the committee so that one can differentiate between 'no object' and
'object'.
 
A

aegis

infobahn said:
Really? I'd have thought it was 'no thing'.


No, in C an object is not a "thing", but a region of storage in
the execution environment.


The abstraction is irrelevant, because the C Standard (which is what
we're on about) doesn't consider cars, bicycles, or planes to be
objects unless they are regions of storage in the execution
environment.


The C Standard doesn't agree with you.


Chapter and verse, please.


If, as you claim, the 'no object' is in fact an object, then
NULL can't point there.

"An integral 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 assigned to or compared for equality to a
pointer, the constant is converted to a pointer of that type. Such a
pointer, called a null pointer, is guaranteed to compare unequal to a
pointer to any object or function."

wow, you have completely missed everything that I have said
thus far. I'm well aware of what the standard defines as
an object. And I even pointed out my other use of object
to talk about the issue in an even more abstract way.
But you have completely failed to realize this. Are you
always this obtuse?
 
K

Keith Thompson

aegis said:
And yes, Keith, 'no object' is a thing.

What part of the word "no" do you not understand?

I have no dog. This "no dog" is not an object, it is not a thing, it
is not a dog. I simply don't have a dog. That's what "no" means.

A null pointer points to no object. That means that a null pointer
does not point to any object. There's no "there" there.
 
I

infobahn

aegis said:
wow, you have completely missed everything that I have said
thus far.

No, I've read as much of the thread as I have received on my newsreader
pretty carefully, because I'm hoping against hope that I might receive
a useful answer to my question. But so far, what you've said doesn't
relate to what the Standard says about objects or NULL.
I'm well aware of what the standard defines as
an object. And I even pointed out my other use of object
to talk about the issue in an even more abstract way.
But you have completely failed to realize this. Are you
always this obtuse?

I realise you are trying to use the word "object" in a way that the
Standard doesn't recognise. You can use the word "object" to mean
anything you like, but as soon as you use it in a way that doesn't
coincide with the Standard's definition, your usage ceases to be
meaningful in a discussion of the behaviour required by the
Standard.
 
A

aegis

Keith said:
What part of the word "no" do you not understand?

I have no dog. This "no dog" is not an object, it is not a thing, it
is not a dog. I simply don't have a dog. That's what "no" means.

A null pointer points to no object. That means that a null pointer
does not point to any object. There's no "there" there.

'no object' is a thing. It is a concept. It allows us to
answer the question
'does this pointer point to an object or no object?'
 
P

Peter Nilsson

aegis said:
Saying a pointer that is a null pointer, points to nowhere,
is not meaningful. And in fact, that is an oxymoron. Because
as a pointer, it will point to something. That is the nature
of a pointer. But the issue is whether or not that something
it points at, is meaningful. And yes, ... 'no object' is
a thing. It is a distinguishable entity crafted by the
committee so that one can differentiate between 'no object'
and 'object'.

So, NO OBJECT is a THING-IN-ITSELF?

I see now...
 
A

aegis

Keith said:
What part of the word "no" do you not understand?

I have no dog. This "no dog" is not an object, it is not a thing, it
is not a dog. I simply don't have a dog. That's what "no" means.

A null pointer points to no object. That means that a null pointer
does not point to any object. There's no "there" there.

'no object' is a thing. It is a goddamn concept. And that concept
is used to define a value that is used with pointers so that
one may get an answer to the question,
'do I point to an object or no object?'

Please engage your brain before posting next time.
 
C

CBFalconer

Keith said:
.... snip ...

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 printf has a problem with this, but the calling code
may. The problems arise while accessing the value of ptr in order
to pass it.
 
K

Keith Thompson

CBFalconer said:
I don't think printf has a problem with this, but the calling code
may. The problems arise while accessing the value of ptr in order
to pass it.

Good point; I withdraw the suggestion.
 
F

frob

CBFalconer said:
As far as the statement
(assuming a proper environment for it) is concerned, printf is
supposed to dump out a representation of a pointer value. NULL is
a suitable value for a pointer. (void *)0 is a means of generating
that NULL value. I see no reason for any implementor to
distinguish it, dereference it, or to criticize its ancestry, so I
should not expect any problems from the snippet.

I disagree.

While a pointer may be assigned to the null pointer value, it's usage by
library functions is either explicitly included or excluded. In this case,
I agree that an implementation probably will not dereference it, since the
behavior is implementation defined. However, 'probably' doesn't cut it; the
standard must clearly state the legality.

I believe this is an accidental undefined behavior, and should be clarified.

There are two reasons I believe this to be the case.


My first reason is that the behavior is, at the very least, ambiguous. The
standard does not specify the output generated from the %p conversion
specifier. It simply states that it will be "converted to a sequence of
printing characters, in an implementation-defined manner." (see 7.19.6.1).

It is important to note that the standard does not specify if the value will
be dereferenced, nor if it must conform to a range (for example, a
legitimate address inside the address space, or the null pointer).

The only constraint is that it must be a pointer to void. The pointer to
void (from 6.2.5) "shall have the same representation and alignment
requirements as a pointer to a character type." We may therefore conclude it
has a specific representation and alignment, but nothing more.

Several thread responses have asserted that the lack of a range implies that
any pointer value may be used. I tend to agree, and my testing on several
compilers shows that the tested implementations concluded this as well.

However, a gut feeling and real-world implementations do not equate to the
actual specification.

As the actual standard does not include a range requirement, we may conclude
that any properly aligned value, regardless of it being inside the program's
address space or being a null pointer, can be used as an argument. This
also tends to reinforce (but not explicitly state) that the pointer will not
be dereferenced.

So, at the outset, the behavior is ambiguous and possibly undefined, and
therefore requires elaboration.



Now, I will present how the behavior is actually undefined.

We must be very careful about the definitions. Almost all responders have
used their own beliefs about the definitions, rather than using the actual
definitions provided in the standard.

"Each of the following statements applies unless explicitly stated otherwise
in the detailed descriptions that follow: If the argument to a function has
an invalid value (...) or a type (...) not expected by a function with
variable number of arguments, the behavior is undefined." (see 7.1.4)

The definition of invalid values include values outside the domain of the
function, pointers outside the address space of the program, and a null
pointer. Therefore, it must be explicitly stated that the null pointer or a
pointer outside the address space of the program may be used, or else their
use is an undefined behavior.

While I could use that last point as proof enough for my debate, it has
already been contested by others, so I will use another method.

Continuing to 7.19.6.1, the description of the %p conversion specifier, "The
argument shall be a pointer to void. The value of the pointer is converted
to a sequence of printing characters, in an implementation-defined manner."

The actual standard requires "a pointer to void". It does not say (as many
other posters have suggested) "NULL that may be used as a suitable value for
a pointer", "the address of an object", "a pointer that is turned off", "a
pointer pointing to no particular object", "not a dog and not an airplane",
or any of the other analogs presented.

It must be a pointer to void, nothing else, by it's own definition. This is
not ambiguous.

A pointer to void (from 6.2.5) "shall have the same representation and
alignment requirements as a pointer to a character type." There is no
definition expressly allowing the type pointer to void to be compatible with
the NULL pointer constant, nor for a pointer to void to be compatible with a
null pointer.

Referring back to 7.1.4, the question is simply: "Are the null pointer and
the NULL pointer constant proper pointers to void, including the
representation and alignment requirements, and therefore valid values for
this printf conversion specifier?" If not, then it is clearly stated to be
undefined behavior.

Applying the recursive definitions given in 6.2.5, the pointer to void is:
"an object whose value provides a reference to an entity of the void type,
comprising an empty set of values; it is an incomplete type that cannot be
completed."

Continuing with 6.3.2.3, "A pointer to an object or incomplete type may be
converted to a pointer to a different object or incomplete type. If the
resulting pointer is not correctly aligned for the pointed-to type, the
behavior is undefined."

Nowhere in the standard is the null pointer required to have the same
representation and alignment requirements as a pointer to void type, only
that it is implementation defined. The NULL pointer constant is similarly
defined to expand to an implementation defined null pointer constant. (see
7.17)

Therefore, if both the implementation defined values for NULL and the null
pointer are properly aligned as a pointer to void, the results are defined.
Otherwise, this case results in undefined behavior.

Since we cannot assume that one implementation defined behavior (alignment
requirement) will match a second implementation defined behavior (value of
null pointer), when the behaviors are not required by the standard to be
simultaneously met, we must conclude that the test fails. The behavior is
therefore specified as undefined.





I see three possible corrections:

1. Limit the range to valid pointers: The value of the argument must point
to a valid object.

2. Limit the range to within the program, and include null pointers: The
value must be either a valid pointer within the address space of the
program, or a null pointer.

3. Remove the 'pointer to void' requirement: The value must be a constant
integer expression which is converted to a pointer type pursuant to the
rules of 6.3.2.3. (ie, there are no alignment requirements nor address
space requirements, and the value may be a trap representation.)

The first solution would probably break some code, including the case the OP
suggested. The second solution seems to be the intended meaning as written,
although it also breaks a small body of existing code. The third solution
appears to be how the tested implantations performed the operation, but some
implementations may require changes to meet it.

bryanw / frob
 
K

Kevin Bracey

In message <[email protected]>
"Peter Nilsson said:
So, NO OBJECT is a THING-IN-ITSELF?

[spoilers]






















You obviously haven't played the superb Hitchhiker's Guide to the Galaxy text
adventure, in which "no tea" is an item of your starting inventory.

The solution to the final puzzle requires removing your common sense, picking
up some tea, at which point the game says you've dropped the no tea. Then
you pick back up the no tea, thus holding both tea and no tea simultaneously.

This paradox demonstrates your superior intelligence to one of the ship's
robotic doors, which will then open for you.

Of course, removing your common sense works well for c.l.c too, I find.
 
L

Lawrence Kirby

Ooh, good catch!

Assuming it appears in a valid context (within a function, and with a
"#include <stdio.h>" in the right place), a strict reading of the
standard could lead to the conclusion that it's undefined behavior.
However, I think it's clear that it's not *intended* to be undefined
behavior, and the standard can (and IMHO should) be read so that it
isn't. (The output is implementation-defined, of course, but that's
not what your asking about.)

C99 7.1.4, "Use of library functions", says:

Each of the following statements applies unless explicitly stated
otherwise in the detailed descriptions that follow: If an argument
to a function has an invalid value (such as a value outside the
domain of the function, or a pointer outside the address space of
the program,

These two will always produce undefined behaviour before the function is
even called.
or a null pointer,

The case in question. However I read this as a list of possibilities, this
section does NOT make all null pointer arguments to standard library
functions invalid. It cannot do so because there are clarly valid cases
such as fflush(NULL). It is up to the specification of individual
functions as to whether null pointer arguments are valid.
or a pointer to non-modifiable
storage when the corresponding parameter is not const-qualified)

This could be problematic because the corresponding argument for %p is not
const qualified. However the "list of possibilities" issue is the same as
that for a null pointer argument.
or a type (after promotion) not expected by a function with
variable number of arguments, the behavior is undefined.

This just confirms the application of variable argument rules for standard
library functions.
C99 7.19.6.1p8, "The fprintf function" (page 279) says:

p The argument shall be a pointer to void. The value of the
pointer is converted to a sequence of printing characters, in
an implementation-defined manner.

Since this doesn't explicitly say that a null pointer is allowed, one
could argue that it's undefined behavior.

A null pointer is a perfectly valid pointer value. The section above makes
a clear definition of behaviour (albeit implementation-defined) for
pointer values without exception. As such there is no possibility of
undefined behaviour arising from here. I.e. if the code managed to get
this far without invoking undefined behaviour it is fine.
The escape clause, I think is that 7.1.4 says "If an argument to a
function has an invalid value (*such as* ... a null pointer ...)". If I
turn my head to one side and squint, I can read this as saying that a
null pointer can be an invalid value, not necessarily that it always is
one.

This is a reasonable interpretation, and I see no other way of
interpreting it that is compatible with the rest of the standard.
Therefore it is the correct interprtation (given that no other
"compatible" interpretations surface).
On the other hand, the same reasoning could imply that strlen(NULL)
doesn't invoke undefined behavior. We have to use common sense to
determine that printf("%p\n", (void*)0)) is ok but strlen(NULL) is not
-- but some people's "common sense" will lead them to conclude that the
latter should always return 0.

strlen(NULL) is invalid because it violates the specification of strlen(),
specifically:

"The strlen function computes the length of the string pointed to by s."

The specification of strlen() defines no behaviour if the value of s is
not a pointer to a string, i.e. you have undefined behaviour. A null
pointer is not a pointer to a string. There is nothing equivalent that
makes a null pointer invalid for %p.
Realistically, any implementation won't do anything more exotic that
printing some implementation-defined character string.

Still, I think this calls for a DR.

I see only one viable interpretation of the standard.

Lawrence
 
R

Randy Howard

'no object' is a thing. It is a goddamn concept. And that concept
is used to define a value that is used with pointers so that
one may get an answer to the question,
'do I point to an object or no object?'

Please engage your brain before posting next time.

You're on crack, and doing a very weak Dan Pop impression in
the bargain.
 
K

kuyper

frob said:

Per 6.3.2.3p3:

"An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant.55) 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."

An null pointer constant (NPC) has a type, which can be either an
integer type, or "pointer to void", depending upon which way it
qualifies as an NPC. The purpose of NPCs is that when they appear in
certain contexts, they implicitly get converted to a null pointer of
the appropriate type. For instance, in

int i;
int *p=&i;

if(p == 0)
....


The null pointer constant on the right gets special treatment because
of the fourth item in section 6.5.9p2. If it weren't for that item,
this code would constitute a constraint violation. Because of 6.5.9p5,
the null pointer constant gets convert to 'int*', therefore qualifying
as a null pointer.

The other places where NPCs get similar special treatement are in ?:
(6.5.15p3,6) and simple assignment (6.5.16.1p1), and initializers
(6.6p7).

The argument list of a variadic function is not one of the contexts in
which null pointer constants get special treatement. However, (void*)0
qualifies not only as a null pointer constant, it's also a null pointer
in it's own right, because 0 is also an NPC, and 'void*' is a pointer
type, so (void*)0 is a null pointer of type "pointer to void".

The NULL macro is a standard defined macro whose expansion is
guaranteed to be a null pointer expression. The word NULL has no other
special meaning. In particular, your frequent reference to "NULL
pointer" below is meaningless; I believe that what you intend in those
contexts is "null pointer".

A null pointer value is a pointer value, not dereferenceable, but valid
for purposes such as comparison for equality and assignment to a
pointer object. NULL is a macro, whose expansion is a null pointer
constant such as (void*)0. A "null value" is a meaningful phrase; "NULL
value" is meaningless.
While a pointer may be assigned to the null pointer value,

ITYM that a null pointer value may be assigned to a pointer object?
any pointer value may be used. I tend to agree, and my testing on several
compilers shows that the tested implementations concluded this as
well.

How can you test whether code has undefined behavior? How precisely
would you distinguish a pass from a fail? The "undefined behavior"
produced by a given implementation could consist of printing out a
hexadecimal string corresponding to the bit pattern of the pointer,
interpreted as an unsigned integer of the corresponding size - in other
words, indistinguisheable from the behavior when the behavior is
defined.

Undefined behavior can be determined only by analysis, not by
experimentation.
"Each of the following statements applies unless explicitly stated otherwise
in the detailed descriptions that follow: If the argument to a function has
an invalid value (...) or a type (...) not expected by a function with
variable number of arguments, the behavior is undefined." (see 7.1.4)

The definition of invalid values include values outside the domain of the
function, pointers outside the address space of the program, and a null
pointer.

That's not a definition. Definitions of terms in the standard are
identified either by the fact that the term appears as a paragraph
heading in the "Definitions" section, or by the fact that the term
being defined is printed in italic type. Neither of those applies here.
Those were merely examples of invalid values, and null pointers aren't
necessarily invalid values for all standard library functions.
... Therefore, it must be explicitly stated that the null pointer or a
pointer outside the address space of the program may be used, or else their
use is an undefined behavior.

If that were a definition, I would agree; since it's actually only a
list of examples, your conclusion doesn't follow.
Continuing to 7.19.6.1, the description of the %p conversion specifier, "The
argument shall be a pointer to void. The value of the pointer is converted
to a sequence of printing characters, in an implementation-defined manner."

The actual standard requires "a pointer to void". It does not say
(as many

Which (void*)0 qualifies as.
A pointer to void (from 6.2.5) "shall have the same representation and
alignment requirements as a pointer to a character type." There is no
definition expressly allowing the type pointer to void to be compatible with
the NULL pointer constant, nor for a pointer to void to be compatible with a
null pointer.

No, instead there is language indicating that an NPC shall become a
null pointer value when converted to pointer type. There are other
sections which indicate that the result of a cast operation is a value
of the specified type, which in this case happens to be 'pointer to
void'. Therefore, (void*)0 is indeed a null pointer of type 'pointer to
void'.
Referring back to 7.1.4, the question is simply: "Are the null pointer and
the NULL pointer constant proper pointers to void,

There is no unique "null pointer", so your use of the phrase "the null
pointer" is inapprpriate. Each pointer type has its own null pointer
value and each pointer type may have multiple different representations
of the null pointer value. They may all have the same representation
(though that's not required); however, all null pointers are required
to compare equal, and to compare unequal to an pointer to an actual
object or function.

A null pointer value can be a pointer to void, if that's it's type. A
null pointer such as (char*)0 is obviously not of that type. A NPC of
integer type is a pointer to void only if it occurs in one of the
contexts where it gets implicitly converted to 'void*'. However, the
particular NPC (void*)0 starts out as a pointer to void, so it doesn't
need implicit conversion.
Nowhere in the standard is the null pointer required to have the same
representation and alignment requirements as a pointer to void type,

Except, of course, when it has "pointer to void" type.
1. Limit the range to valid pointers: The value of the argument must point
to a valid object.

2. Limit the range to within the program, and include null pointers: The
value must be either a valid pointer within the address space of the
program, or a null pointer.

That's redundant; null pointer values are valid pointer values for
purposes of comparison for equality, and for assignment. They're not
valid for dereferencing or comparison for order, but they are valid for
some purpose. In the absence of further qualification, the phrase
"valid pointer" must be satisfied by null pointers. The phrases
"pointer value valid for derefencing" or "pointer value valid for all
possible uses" would not include null pointer values; but that's not
the phrase that was used.
3. Remove the 'pointer to void' requirement: The value must be a constant
integer expression which is converted to a pointer type pursuant to the
rules of 6.3.2.3. (ie, there are no alignment requirements nor address
space requirements, and the value may be a trap representation.)

That wouldn't work; since the relevant argument is a variadic one,
printf() needs to know what type it is, in order to correctly extract
it. Since pointers to different types can be of different sizes, it
wouldn't have any way to know how many bytes to extract. Pointers to
different types can also have different representations, even if they
have the same exact size. Only by restricting the argument to a
specific pointer type is is possible for printf() to do it's job.
 
F

Flash Gordon

aegis said:
'no object' is a thing. It is a concept. It allows us to
answer the question
'does this pointer point to an object or no object?'

There is no need for any such thing to exist in order to do that. To see
if the address "34 Hobson Street" is a null pointer all I have to do is
look at the bit pattern. I don't have to treat it as a pointer.

You are also failing to take in to account that the space available for
C pointers to point to is finite, therefore any pointer outside that
range does not point at anything because there is nothing to point to.

It's a bit like the question "what happened before the start of time?"
If time had a definite start then such a question is meaningless because
there was no before, the same applies to C pointers. Null is a defined
value which can be written down and compared against (just as you can
compare a time value against 5 seconds before the start of time) but
null pointers still don't point at anything (or any time) because there
is no place (or time) for them to point to.

Or another analogy (no analogy being perfect) with C we are back in the
days of the flat earth where if you go too far you fall off the edge,
except with C that can be rather more true.

Another possible analogy, point at something with your finger. Then
close you fist and put your hand by your side and ask something what you
are pointing at. The most likely response is something like, "you are
not pointing at anything." However, you (the pointer) still exist, you
are just not pointing at anything.
 

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

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top