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

A

Antoine Leca

[Writing from comp.std.c point of view.]

En (e-mail address removed), Andrey Tarasevich va escriure:
The specification of 'printf' does not say that passing null
pointers to it is OK.

So it is UB.
That's exactly what constitutes the problem.

Where is it a problem?
Put in other words, what exactly is missing when one cannot print a value
for a null pointer?

I think it would be _much_ better to have specific code to deal with null
pointers, rather than receive random ones with fscanf(), for instance.


Antoine
 
F

frob

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

In each of these (assignment, conditional, and initializers), they are
considering the NPC as an implementation defined value (7.17), not as a
pointer to an object. That special value is implementation defined, has no
alignment or representaional requirements specified, and therefore not
necessarily a pointer to void as per the requirement in 6.2.5, p26.

In other words, they have restricted range to one of an actual pointer to
void, or the null pointer.

6.3.2.3p3 specifies that the NPC is unequal to any object or function, not
that it must be a pointer to void, and 6.3.2.3p5 specifies that a pointer
type may contain data that is not correctly aligned, might not point to an
entity of the given type, and might be a trap representation. So there is
no requirement that every value which can be contained in a pointer to void
must actually be a pointer to void. Simply because an object can be
contained in a type does not mean that other values cannot be stored there
as well.

The NPC is implementation defined. It may, or may not, fit the
requiremnents of a pointer to void given in 6.2.5p26.

As a example, consider a machine requiring even alignment for a pointer to
character. Even though the object is capable of storing an odd value, it
does not make the odd value into a pointer to character. (see 6.3.2.3) The
NPC could be defined as an odd value, since this satisfies it's requirements
of not comparing equal to a pointer to any object or function given in the
same section. So in this case, the NPC does not fit the requirements for a
pointer to void.
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.

However, it's alignment and representation is not specified. If you find
that it is, please cite the section.
Undefined behavior can be determined only by analysis, not by
experimentation.

As I imeditaly followed, "real-world implementations do not equate to the
actual specification.", as we both know.
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.

Why not? There is no exception stated, and the standard explicitly states
the responses of the other library functions when a null pointer value is
used as a valid value. From 7.1.4, if it is an invalid value, it is
undefined behavior. In this case, it does not explictly state whether the
NPC is valid or not, which is the source of the ambiguity.
If that were a definition, I would agree; since it's actually only a
list of examples, your conclusion doesn't follow.

It says "invalid value". The question is whether the not the null pointer
is invalid in this case. I argue that it is not a valid value, therefore an
undefined behavior.
Which (void*)0 qualifies as.

Please cite the section of the standard stating that the NPC is a pointer to
void.

Yes, a pointer to void can be assigned a value of the NPC, but that does not
make the NPC into a pointer to void.

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

This is demonstrably false --- the null pointer constant does not
necessarily meet the alignment and representational requirements already
mentioned. As the earlier illustrative example stated, the null pointer
could be an odd value (7.17), where the alignment of pointers to character
and pointer to void are required to have an even value (by virtue of
6.2.5p26). Both could be contained in a pointer to void, but that does not
mean that the void pointer has the alignment and representation required.

Put another way, a pointer to void may be said to contain a any of these
three:
-- A (properly aligned) pointer to void
-- An integer that has been converted to a pointer but is not a pointer to
void
-- The NPC value.
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.

Again, please cite that requirement. The NPC is implementation defined, and
needn't be valid in any particular type; it only must compare unequal to a
pointer to any object or function (6.3.2.3p3).

Except, of course, when it has "pointer to void" type.

Please find a citation. Again, pointer to void has an alignment constraint,
the NPC does not.


Unless you can demonstrate that the NPC (implementation defined) is required
to be of the same alignment of a pointer to void / pointer to character
(also implementation defined), the behavior must be undefined by nature of
the definitions.

frob.
 
C

Chris Torek

[enormous amounts of snippage]
... (void*)0 qualifies as [a pointer to void].

Please cite the section of the standard stating that the NPC is a pointer to
void.

Yes, a pointer to void can be assigned a value of the NPC, but that does not
make the NPC into a pointer to void.

I believe (e-mail address removed) means such things as:

extern void foo(void *);
void f(void) {
void *p = 0;
foo(p);
}

Exercise: what value is passed to function foo()? What if we write:

foo((void *)0);

instead of foo(p); does anything change?

I occasionally find myself wishing that Dennis had defined C, ages
ago, with an actual "nil" keyword or similar, and that this nil
was the only kind of "null pointer constant" that C had. The
problem of distinguishing between "null pointer constant" and "null
pointer" would virtually vanish entirely: "nil" would be *the*
null-pointer-constant, and (void *)nil, (int *)nil, (char *)nil,
and (double (*)(int))nil would be four examples of null pointers.
Moreover, it would be obvious that:

printf("%p\n", nil);

requires a compile-time diagnostic ("null pointer constant with
insufficient context to determine the required kind of null pointer
to construct"), and we would only have to argue about:

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

and:

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

here.
 
M

Michael Mair

frob said:
In each of these (assignment, conditional, and initializers), they are
considering the NPC as an implementation defined value (7.17), not as a
pointer to an object. That special value is implementation defined, has no
alignment or representaional requirements specified, and therefore not
necessarily a pointer to void as per the requirement in 6.2.5, p26.

In other words, they have restricted range to one of an actual pointer to
void, or the null pointer.

6.3.2.3p3 specifies that the NPC is unequal to any object or function, not
that it must be a pointer to void, and 6.3.2.3p5 specifies that a pointer
type may contain data that is not correctly aligned, might not point to an
entity of the given type, and might be a trap representation. So there is
no requirement that every value which can be contained in a pointer to void
must actually be a pointer to void. Simply because an object can be
contained in a type does not mean that other values cannot be stored there
as well.

The NPC is implementation defined. It may, or may not, fit the
requiremnents of a pointer to void given in 6.2.5p26.

Er... are you saying that a definition of NULL as (void *) 0 is not
sufficient to make NULL a constant of type void *?

As a example, consider a machine requiring even alignment for a pointer to
character.

This means that for char *p, ((char *)&p)+i, i odd, is not a valid
address for a char * -- is this really what you want to say?
Even though the object is capable of storing an odd value, it
does not make the odd value into a pointer to character. (see 6.3.2.3)

However, here, you are trying to say that sizeof char != 1 which is
clearly nonsense. A char is by definition the smallest addressable
unit which is why its size is one byte. Pointers to char obviously
must be able to hold "even" and "odd" values.
If you are talking about char **, you may be right under the above
assumption but this does nothing to prove your point.
The
NPC could be defined as an odd value, since this satisfies it's requirements
of not comparing equal to a pointer to any object or function given in the
same section. So in this case, the NPC does not fit the requirements for a
pointer to void.

As alignment and representation of void * are equal to those of char *,
this argument is nonsense.

However, it's alignment and representation is not specified. If you find
that it is, please cite the section.

???
A null pointer constant's representation is implementation defined.
(int *) 0 can have a representation differing from (void *) 0.
What is your point?


[snip]

Please cite the section of the standard stating that the NPC is a pointer to
void.

Yes, a pointer to void can be assigned a value of the NPC, but that does not
make the NPC into a pointer to void.

Not "the" -- "a".
You seem to believe that there is only one null pointer constant.
(void *) 0 is in my eyes of type void * and gives me a null pointer
constant.
Maybe I am constantly missing your point.

This is demonstrably false --- the null pointer constant does not
necessarily meet the alignment and representational requirements already
mentioned. As the earlier illustrative example stated, the null pointer
could be an odd value (7.17), where the alignment of pointers to character
and pointer to void are required to have an even value (by virtue of
6.2.5p26). Both could be contained in a pointer to void, but that does not
mean that the void pointer has the alignment and representation required.

This example was crap and I doubt you can construct a convincing one.

Put another way, a pointer to void may be said to contain a any of these
three:
-- A (properly aligned) pointer to void

I guess your notion of alignment is off the mark.
-- An integer that has been converted to a pointer but is not a pointer to
void

Are you talking about a trap representation? Or about values which
are say out of the bounds of physical memory? Even/odd is no
possibility.
-- The NPC value.

You seem to imply that void * has to be accessed either as void * or
differently, depending on what value the representation holds.
Is this really what you mean?

Again, please cite that requirement. The NPC is implementation defined, and
needn't be valid in any particular type; it only must compare unequal to a
pointer to any object or function (6.3.2.3p3).

See above: The/a.
A typecast gives an explicit conversion to the specified type.

Please find a citation. Again, pointer to void has an alignment constraint,
the NPC does not.

I think _you_ need some citations to prove your notion that a value
preceded by a typecast does not yield a value of the type cast to.

Unless you can demonstrate that the NPC (implementation defined) is required
to be of the same alignment of a pointer to void / pointer to character
(also implementation defined), the behavior must be undefined by nature of
the definitions.

If we use a null pointer constant given by (char *) 0 or one given by
(void *) 0 and assign it to char */void *, respectively, there is no
question about alignment.


-Michael
 
J

James Kuyper

frob said:
In each of these (assignment, conditional, and initializers), they are
considering the NPC as an implementation defined value (7.17),

The special rules for NPC apply to any NPC, not just the one that is
implementation-defined as the expansion of the NULL macro.
... not as a
pointer to an object.

However, I do agree: none of those rules apply to a pointer to an
object; such a pointer would not qualify as an NPC.
... That special value is implementation defined, has no
alignment or representaional requirements specified, and therefore not
necessarily a pointer to void as per the requirement in 6.2.5, p26.

That implementation-defined macro (not value) is required to expand into
a null pointer constant. All null pointer constants, regardless of
whether they're the result of expanding NULL, or produced explicitly by
by user code, are required to convert into null pointers of the
specified type when converted to a pointer type. Null pointer constants
are special in that they implicitly convert into a pointer type under
circumstances where (without the special rules for NPCs) they might not
so convert.

If the explicit or implicit pointer conversion is to 'void*', as is the
case with (void*)0, then the resulting null pointer is a pointer to void.
6.3.2.3p3 specifies that the NPC is unequal to any object or function,

"compares unequal to a pointer to any object or function". When you
leave out the phrases "compares" and "a pointer to", you change the
meaning. An NPC is not only allowed, but required, to compare equal to
the value of an object, if that object has a pointer type and a null value.
... not
that it must be a pointer to void,

I never suggested that all NPCs are required to be pointers to void.
However, when an NPC is converted to a pointer type, the result is a
null pointer of that type; if the specified type is "pointer to void",
it meets the requirements of 6.2.5p26. '(void*)0' is both an NPC in it's
own right, and an NPC (0) converted to a pointer type (void*). It is
therefore both an NPC and an null pointer value. However,
... and 6.3.2.3p5 specifies that a pointer
type may contain data that is not correctly aligned, might not point to an
entity of the given type, and might be a trap representation. So there is
no requirement that every value which can be contained in a pointer to void
must actually be a pointer to void.

'pointer to void' is a type. Whether or not a value is a 'pointer to
void' depends only upon the type of the expression that created the
value; it doesn't depend upon the value itself.

An NPC is a source code construct which when converted to pointer type
results in a null pointer value of the specified type. It is not an
object; only objects can have trap representations. It is a value which
is explicitly described by the standard as being valid for use in a
comparisons for equality, in addition to several other uses. Therefore
it is, in a limited sense, a valid pointer (it is not a valid pointer
for dereferencing, for instance).
The NPC is implementation defined.

No, NPC is standard-defined - paragraph 6.3.2.3p3. The fact that "null
pointer constant" is in italics is how you can identify the fact that
this paragraph constitutes the definition of that term.
It may, or may not, fit the
requiremnents of a pointer to void given in 6.2.5p26.

Agreed - whether or not it fits those requirements depends upon whether
it contains an explicit cast to 'void*'; that cast guarantees that it is
indeed a pointer to void.
As a example, consider a machine requiring even alignment for a pointer to
character.

That's not allowed. The alignment of character types is required to be
'1'. I'll assume, for the sake of your argument, that you're actually
talking about a pointer to a type larger than char.
... Even though the object is capable of storing an odd value, it
does not make the odd value into a pointer to character. (see 6.3.2.3)

Agreed; an arbitrary pointer value is not necessarily correctly aligned
for conversion to a pointer of an arbitrary type. However, any pointer
value can be converted to a pointer to void; 'void' is not allowed to
have alignment restrictions. 'pointer to void' is allowed to have
alignment restrictions, but since we're not talking about pointer
objects, but only pointer values, the alignment restrictions of the
pointer type never come into play. Only the alignment restrictions of
the pointed-at type matter, and 'void' isn't allowed to have any.

In any event, a null pointer of any type may be freely converted to a
null pointer of any other type.

The
NPC could be defined as an odd value, since this satisfies it's requirements
of not comparing equal to a pointer to any object or function given in the
same section. So in this case, the NPC does not fit the requirements for a
pointer to void.

An NPC can't be an odd value, because only arithmetic types are
meaningfully 'odd' or 'even'. An expression with arithmetic type can
qualify as an NPC only if it has a value of 0 and an integer type, and
must therefore be even.

Now, if an NPC doesn't have integral type them it must have the type
'pointer to void', and in principle it could be represented by an
address with odd alignment. However, I can't think of any way in which
portable code could detect the fact. Every operation that depends upon
the alignment of a pointer is meaningless when applied to a null pointer
value.
In practice, if an implementation gives some meaning to the alignment of
a null pointer value, I'd expect it to give it an alignment compatible
with all possible types, just like the other value return values from
malloc(); I'd be (slightly) surprised by any implementation where this
wasn't true.
However, it's alignment and representation is not specified.

Agreed; the type of NULL isn't specified. It can be any integer type, or
'void*', since NULL must expand into an NPC, and those are the only two
options for NPCs. However, the type of (void*)0 is specified by the
standard, section 6.5.4p4: "Preceding an expression by a parenthesized
type name converts the value of the expression to the named type."
Why not? There is no exception stated,

Which would be relevant if it were a definition. Since it's just a list
of examples, the absence of a exception doesn't mean that there aren't
any. Each function has it's own set of valid values, some of which are
explained explicitly, others of which are implicit. If a function is
described as dereferencing a pointer, it's implicitly required to be
dereferenceable. If it's described as comparing the pointer to another
pointer for equality, it's implicitly required to be
equality-comparable. If it's describes as being compared for order with
another pointer, the pair of pointers is required to be comparable for
order. If it's described as writing through the pointer, it's implicitly
required that the destination be writable. printf() makes no such
restrictions on the pointer that matches a "%p" format code, so none of
those implicit requirements apply.

It says "invalid value". The question is whether the not the null pointer
is invalid in this case. I argue that it is not a valid value, therefore an
undefined behavior.

Yes, but you argue invalidly.
Please cite the section of the standard stating that the NPC is a pointer to
void.

I never said that all NPCs are pointers to void. The citation for this
particular NPC is 6.5.4p4.
Yes, a pointer to void can be assigned a value of the NPC, but that does not
make the NPC into a pointer to void.

Correct. What makes it a pointer to void is the explicit cast to
'void*', not the fact that it also qualifies as an NPC.
This is demonstrably false --- the null pointer constant does not
necessarily meet the alignment and representational requirements already
mentioned.

You've got cause and effect reversed. You don't deduce that it's void*
because it meets those requirements; you deduce the fact that it meets
those requirements from the fact that it's required (by 6.5.4p4) to be a
void*.

....
Put another way, a pointer to void may be said to contain a any of these
three:
-- A (properly aligned) pointer to void

How can a pointer to void contain a pointer to void? That way leads to
infinite recursion. In standardese, there's a distinction between the
pointer and the address. The pointer is a C concept; the address is a
machine language concept. A C pointer typically contains a machine
address; in some implementations it contains other things as well. I
think that the standardese corresponding to what you intend to mean here
is "a valid address". However, I'm not sure what you mean. I don't know
how to even talk about the next two "options" you list, until you can
convert what you're talking about into standardese.

....
Again, please cite that requirement.
6.5.4p4.


The NPC is implementation defined,

Incorrect: the rules for NPCs (plural, NOT singular) are
standard-defined in 6.3.2.3p3. The particular NPC that 'NULL' expands
into is implementation-defined, but NPCs in general aren't.
... and
needn't be valid in any particular type;

Per that section, it must either be an integer expression with a value
of 0, in which case it has an integer type, or it must be such an
expression, converted to 'void*', in which case it has type 'void*'.
There's no other options allowed by that section.
Please find a citation.

6.5.4p4.
 
K

Keith Thompson

frob said:
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.

Agreed. Most of the silly analogies came up because one poster seemed
to be having trouble understanding what a null pointer is (see the
"'no object' is an object" subthread). The argument must be of type
void* (or probably a pointer-to-character, but that's irrelevant to
the current discussion).
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.

It's very important to understand that a "null pointer constant" and a
"null pointer" are two very different things. A null pointer constant
is a construct that can appear in C source code. A null pointer is a
value (of some pointer type) that can exist in a running program.

The first half of your statement, that "[t]here is no definition
expressly allowing the type pointer to void to be compatible with the
NULL pointer constant", is basically correct. More precisely, if the
argument to printf() assocated with "%p" is a null pointer constant,
the resulting argument that's passed at run-time is not necessarily
compatible with type void*. All that means is that this:
printf("%p\n", NULL);
may invoke UB, and should be replaced with
printf("%p\n", (void*)NULL);
This is a fairly trivial point, and is not central to the current
discussion.

The second half, that there is no requirement "for a pointer to void
to be compatible with a null pointer", doesn't make much sense. There
is a null pointer value of type void*; of course that value is
compatible with type void*. As above, passing that value to printf()
may require a little extra work, such as a cast, in some cases.

The question under discussion is whether passing a null pointer value
of type void* to printf with a "%p" format invokes undefined behavior.
The subject line of this thread shows one straightforward way to do
that. All this talk about compatibility is a distraction from that
question; if someone asked about it in a separate thread, I'd refer
the poster to sections 4 and 5 of the C FAQ.

This entire discussion could have taken place nearly without reference
to passing a null pointer constant or the NULL macro to printf(). For
example:

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

null_ptr is a reference to an object of type void*, so there are no
type conversion issues. The object was initialized with a null
pointer constant of type void* (the cast is actually unnecessary), so
we know its value is a null pointer; it's declared as a void* object,
so we know its value is a null pointer of type void*. If you have
any issues with the original problem statement:

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

that don't apply to my example above, those issues are not relevant to
this thread.

[...]
I see three possible corrections:

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

That would break existing code that uses printf's "%p" format for
pointer values that may or may not be null pointers. The "%p" format
is intended to print the value of a pointer; there's no reason for it
not to work with a null pointer value. Typical implementations
(possibly all implementations) do no extra work to support this.
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.

I'm not sure what "within the address space of the program" means.
A program has no contiguous address space as far as the C abstact
machine is concerned.

The way I'd express it is that any value of type void* that can be
evaluated without invoking undefined behavior can be passed to
printf("%p", ...) without invoking undefined behavior.
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.)

Huh? You're talking about changing the type of the argument (to
what?) and requiring it to be a constant integer expression (why bring
integers into it?). With this requirement, you couldn't use
printf("%p", ...) to print the value of a pointer variable. I presume
that's not what you meant. What did you mean?

printf(), when it sees a "%p" specifier, is going to do the equivalent
of va_arg(..., void*), which means the caller has to have passed a
void* argument. printf() doesn't, and shouldn't, care how that
argument was constructed, as long as it's valid.
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.

The second solution (as I restated it) doesn't break any code that
isn't broken already. It allows a null pointer, a pointer to an
object, or a pointer just past the end of an object. Any other
pointer value is invalid; the *caller* will invoke undefined behavior
just by evaluating it, before printf() is even called.
 
K

Keith Thompson

Michael Mair said:
frob wrote: [...]
The NPC is implementation defined. It may, or may not, fit the
requiremnents of a pointer to void given in 6.2.5p26.

Er... are you saying that a definition of NULL as (void *) 0 is not
sufficient to make NULL a constant of type void *?

A definition of NULL as (void*)0 does make NULL a constant of type void*.
A definition of NULL as 0 does not make NULL a constant of type void*.
Either definition is allowed.

Because of this,
printf("%p\n", NULL);
invokes undefined behavior (or at least ambiguously does so), because
the argument isn't necessarily of type void*; it could be of type int.

That's why nobody is talking about
printf("%p\n", NULL);
Instead, we're talking about
printf("%p\n", (void *)0);
which unambiguously passes a null pointer of type void* to printf().

The first example demonstrates a trivial error whose solution is in
the FAQ. The second demonstrates an ambiguity in the standard, which
is what we're actually discussing.
 
K

Keith Thompson

James Kuyper said:
frob wrote: [...]
I never suggested that all NPCs are required to be pointers to
void. However, when an NPC is converted to a pointer type, the result
is a null pointer of that type; if the specified type is "pointer to
void", it meets the requirements of 6.2.5p26. '(void*)0' is both an
NPC in it's own right, and an NPC (0) converted to a pointer type
(void*). It is therefore both an NPC and an null pointer
value.

A minor quibble: (void*)0 is a source construct; a null pointer value
exists only during execution of the program. Usually we can keep
these things straight from context, but in this case the distinction
is central to this sub-discussion. (void*)0 is an NPC which results
in a null pointer value at runtime.

[...]
How can a pointer to void contain a pointer to void? That way leads to
infinite recursion. In standardese, there's a distinction between the
pointer and the address. The pointer is a C concept; the address is a
machine language concept. A C pointer typically contains a machine
address; in some implementations it contains other things as well. I
think that the standardese corresponding to what you intend to mean
here is "a valid address". However, I'm not sure what you mean. I
don't know how to even talk about the next two "options" you list,
until you can convert what you're talking about into standardese.

Actually, I think the standard tends to use the terms "address" and
"pointer" almost interchangeably. A "pointer" can be either an object
of pointer type or a value of pointer type. An "address" is a value
of pointer type, probably the address of some object. (We had a long
discussion of this not long ago.)

frob was saying that a pointer to void may contain a pointer to void;
this actually makes sense if the first "pointer to void" refers to an
object of type void*, and the second refers to a value of type void*.

This is another of those things we usually infer from context, but in
this case the object vs. value distinction is central to the
discussion and should be stated explicitly.
 
A

aegis

Flash said:
aegis said:
Keith 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.


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

Irrelevant. Such a thing exists for the very purpose I mentioned.
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.

Whether there are a finite amount of resources or an infinite amount
is irrelevant. Pointing to 'no object' is a meaningful concept crafted
by the standard to distinguish between pointing at an object and no
object.
The whole point of this, is because Keith said a null pointer points
'nowhere'. And I pointed out that, that is not true. This is
because NULL is a meaningful thing that represents 'no object'.
It is an entity in an of itself. Not something that is insignificant
or does not exist.
 
K

Keith Thompson

Lawrence Kirby said:
I see only one viable interpretation of the standard.

Still, it would be nice if the non-viable interpretations were
explicitly excluded rather than excluded by implication.

The standard, as it's currently written, is a bit too easy to
misinterpret as allowing only a non-null pointer for the "%p"
specifier. 7.1.4 weakly implies that null pointers are invalid; I'd
be more comfortable if that weak implication were overridden by an
explicit statement in 7.19.6.1.

(Perhaps the "such as" clause in 7.1.4p1 belongs in a footnote.)
 
K

Keith Thompson

Antoine Leca said:
[Writing from comp.std.c point of view.]

En (e-mail address removed), Andrey Tarasevich va escriure:
The specification of 'printf' does not say that passing null
pointers to it is OK.

So it is UB.
That's exactly what constitutes the problem.

Where is it a problem?
Put in other words, what exactly is missing when one cannot print a value
for a null pointer?

I think it would be _much_ better to have specific code to deal with null
pointers, rather than receive random ones with fscanf(), for instance.

I disagree. I often use "%p" to show the value of a pointer. The
displayed value typically can't be used by the program itself; it's
for my information. Much existing code presumably uses "%p" to
display pointer values that may or may not be null.

A typical implementation (possibly all implementations) already
supports printf("%p\n", (void*)0) with no extra effort; despite my
off-the-wall proposal, there's no reason for printf to do try to
dereference the pointer or do anything else other than examining it as
an integer value or as a sequence of bytes.

Even if it were decided that printf("%p\n", (void*)0) invoked
undefined behavior, implementations would continue to behave as they
do now; it would be an instance of UB that's practically never detected.
 
K

Keith Thompson

Keith Thompson said:
I'm not sure what "within the address space of the program" means.
A program has no contiguous address space as far as the C abstact
machine is concerned.

I see now that the standard uses the phrase "a pointer outside the
address space of the program," in 7.1.4p1 as an example of an invalid
argument. I still don't think that means anything in terms of the C
abstract machine, but the ambiguity is in the standard, not in what
frob wrote.
 
K

Keith Thompson

aegis said:
Whether there are a finite amount of resources or an infinite amount
is irrelevant. Pointing to 'no object' is a meaningful concept crafted
by the standard to distinguish between pointing at an object and no
object.
The whole point of this, is because Keith said a null pointer points
'nowhere'. And I pointed out that, that is not true. This is
because NULL is a meaningful thing that represents 'no object'.
It is an entity in an of itself. Not something that is insignificant
or does not exist.

I was going to drop this, but ...

If what a null pointer points to isn't something that does not exist,
what is? Why do we have the phrase "does not exist" in the language?
 
F

frob

I was unclear in my previous post. I will attempt to clear it up.

When I spoke of a machine with even boundaries for char types, I was
referring to the actual executable code after the translation from C has
occurred. I was not referring to the C program source. I apologize for
being unclear on that point. I was not trying to state or imply that a
char* +1 would always be even in the C language. I was referring entirly to
the translated code for the execution environment.


The specific wording I am referring to is "same representation and alignment
requirements", given in 6.2.5.


When the standard refers to "same representation and alignment
requirements", it states in the footnote that this "implies
interchangability as arguments to functions, return values from functions,
and members of unions", I am forced to believe the wording is referring to
the implementation defined actual layout of the bits in the execution
environment, not the conceptual model in the C language source. I cannot
find any actual definition which leads me to believe otherwise.

The standard requires that the type "pointer to void" has these specific
"representation and alignment requirements", which are implementation
defined.

Also, as you and others have pointed out, NPCs may be of varied types and
are implementation defined.

Nowhere in the standard can I find that (void*)0 has or can be converted to
the "same representation and alignment requirements" as specified for the
purposes of a pointer to void. This is implied in several cases, but never
explicitly stated (to my knowledge). The 6.3.2.4p4 statement, "Conversion
of a null pointer to another pointer type yields a null pointer of that
type." This is the closest I can find, but it does not meet the exact
wording given in 6.2.6p26, respecting the "same representation and alignment
requirements."

Yes, it is a very small detail, and yes, it probably should be interpreted
as meeting those requirements. However, if we are going to be splitting
hairs over the standard, I will refer to the definition of 'implies' in
logic, which fails in this exact case.

For the library functions that explicitly state the behavior on a null
pointer, I can expect the NPCs to have a specified result, by definition.
For those functions that don't require a pointer to an object, but simply a
pointer to void, to be strictly legal I must be able to assert that the NPC
have the "same representation and alignment requirements" for the pointer to
void. (these are the three ways for the 'implies' logic to be true.) If we
cannot assert this, then the implication fails, (the fourth element of the
'implies' logic) and the behavior must be undefined.

Because I cannot find any statements requiring that the "same representation
and alignment requirements" for a pointer to void type are met in this
specific case, and since both are implementation defined, I am concluding
that the requirements could theoretically not be met.

If, somewhere in the definitions, the constraints of being the "same
representation and alignment requirements" of a pointer to void, as
specified by 6.2.5p26 are explicitly met with a statement of (void*)0, then
I agree that the behavior is well defined but poorly worded.

However, since I can find no assurance that this is true, except by
anecdotal evidence and statements of implication and infererence present
throughout the standard, I am not completely convinced that a NPC cannot be
an "invalid value" as specified in 7.1.4, since the conversion specifier
requires that the "argument shall be a pointer to void", with it's
corresponding constraints.

Yes, I am splitting hairs. Yes, it probably is not a significant thing and
we have probably wasted more time discussing it that is warranted. Yes, the
behavior is probably intended to be legal and will probably be considered
legal in the implementations. However, I have not yet been completely
assured that it is strictly legal.

frob.
 
M

Michael Mair

Keith said:
Michael Mair said:
frob wrote:
[...]
The NPC is implementation defined. It may, or may not, fit the
requiremnents of a pointer to void given in 6.2.5p26.

Er... are you saying that a definition of NULL as (void *) 0 is not
sufficient to make NULL a constant of type void *?


A definition of NULL as (void*)0 does make NULL a constant of type void*.
A definition of NULL as 0 does not make NULL a constant of type void*.
Either definition is allowed.

[snip]

I am aware of that -- I just am not sure whether "frob" is aware of
the fact that there is _not_ one and only one null pointer constant.
IMO, his constant use of "the" implies the opposite.


Cheers
Michael
 
A

aegis

Keith said:
I was going to drop this, but ...

If what a null pointer points to isn't something that does not exist,
what is? Why do we have the phrase "does not exist" in the language?

Is the phrase in the language 'does not exist' pertinent to
what a null pointer points to?
 
J

James Kuyper

frob said:
I was unclear in my previous post. I will attempt to clear it up.

When I spoke of a machine with even boundaries for char types, I was
referring to the actual executable code after the translation from C has
occurred. I was not referring to the C program source. I apologize for
being unclear on that point. I was not trying to state or imply that a
char* +1 would always be even in the C language. I was referring entirly to
the translated code for the execution environment.

That sounds reasonable in itself. However, the above comments (assuming
that you and I are both understanding them the same way) render your
previous argument about an NPC being 'odd' meaningless.

On a machine where addresses are required to point to word boundaries,
with words being two bytes long (a not uncommon architecture) then a
char* pointer will have to be represented by the combination of a
machine address plus some mechanism for determining which byte of the
word the character is in. On some platforms, a high-order bit within the
machine address is used by the C implementation for this purpose; this
requires that the chosen bit is not used by the machine itself. When
that approach is not feasible, it generally means that if a type T is
word aligned, then sizeof(char*) > sizeof(T*). A pointer to void is
required to have the same representation as 'char *', and will therefore
be just as easily capable of representing "odd" addresses as a 'char*'
pointer itself.

....
Nowhere in the standard can I find that (void*)0 has or can be converted to
the "same representation and alignment requirements" as specified for the
purposes of a pointer to void. This is implied in several cases, but never
explicitly stated (to my knowledge). The 6.3.2.4p4 statement, "Conversion
of a null pointer to another pointer type yields a null pointer of that
type." This is the closest I can find, but it does not meet the exact
wording given in 6.2.6p26, respecting the "same representation and alignment
requirements."

Keep in mind that objects contain represent ions of values; the value
'1' doesn't have to have a specific representation as far as C is
concerned, until it's actually stored in an object. In actual practice,
there are real machines where registers use different representations
than RAM for certain types, in which case loading the value from RAM
into a register requires a change of representation.

When it's permitted for a type to have multiple representations for a
given value (as is the case for null pointer values), there's no
guarantee that storing the same value in different objects, or in the
same object at different times, will result in the same representation
being created. Therefore, it's meaningless to ask what "the"
representation of (void*)0 is. However, an implementation is required to
create a valid representation in any 'void*' object that (void*)0 is
stored in.

You're not supposed to conclude that it qualifies as a pointer to void
because it meets those representation and alignment requirements when
stored in an object; it hasn't been stored in an object yet (and
depending upon context, it might never be), so there's no way to tell.
What you can infer is that it will meet those requirements if it gets
stored in an object because it's required (as a result of the 'void*'
cast) to be a null pointer value of type pointer to void. Since the
conversion is guaranteed to succeed, and the result of that conversion
is guaranteed to be usable in various contexts, it must also be
considered valid, at least in a limited sense. Therefore, when it gets
stored in an object the implementation is required to select a valid
representation for the value, one that matches one of the valid
representations for a 'char*' pointer with the same value (i.e., a null
pointer value).
Yes, it is a very small detail, and yes, it probably should be interpreted
as meeting those requirements. However, if we are going to be splitting
hairs over the standard, I will refer to the definition of 'implies' in
logic, which fails in this exact case.

This isn't splitting hairs; it's trying to perform the inference exactly
backwards, which is doomed to failure. (void*)0 has type 'pointer to
void' for precisely the same reason that (long)3 has type 'long int'. If
you try to find the part of the standard which describes the
representation of (long)3, to determine whether or not it meets the
requirements of 6.2.6.2, you'll have just as much trouble as you had
looking for text describing the representation and alignment of
(void*)0, and for precisely the same reasons.

It's up to the implementation to ensure that the representation for
(void*)0 meets the requirements of 6.2.5p26 for pointers to void when it
gets stored in a void* object. That's for the same reasons that it's the
responsibility of the implementation to ensure that (long)3 meets the
requirements of section 6.2.6.2 for the representation of integer types
when it gets stored in a 'long' object.
 

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,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top