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

W

Wojtek Lerch

Trevor L. Jackson said:
No. As I understand it the language of the standard describes those
values as either indeterminate OR trap values. Thus I used a term that
could apply to both kinds of invalid values.

No, the language of the standard describes them as indeterminate values.
The standard does not use the term "trap value", only "trap representation".
And a trap representation is a special case of an indeterminate value.
"Indeterminate value" is defined as "either an unspecified value or a trap
representation".
 
K

Keith Thompson

Trevor L. Jackson said:
No. As I understand it the language of the standard describes those
values as either indeterminate OR trap values. Thus I used a term
that could apply to both kinds of invalid values.

Not quite.

First, just to make sure we're talking about the same thing:

int *ptr = malloc(sizeof *ptr);
assert(ptr != NULL);
free(ptr);

The value of ptr after the free() is what we're referring to as
"stale", right? (A stale pointer can also be one that pointed to a
local variable that has ended its lifetime, e.g., because the function
that defines it has returned.)

The standard says that this pointer value is "indeterminate" (C99
6.2.4p2), which means that it's "either an unspecified value or a trap
representation" (3.17.2). An "unspecified value" is a "valid value of
the relevant type where this International Standard imposes no
requirements on which value is chosen in any instance" (and a note
says that "An unspecified value cannot be a trap representation")
(3.17.3). A "trap representation" is one that causes undefined
behavior if it's read by an lvalue expression that does not have
character type (6.2.6.1p5).
 
R

Richard Bos

Douglas A. Gwyn said:
Sure it is, since for many of the functions a null pointer
value has its own explicit semantics. With your reading,
those would all be invalid values, resulting in undefined
behavior, in which case it makes no sense for the behavior
to be specified. Thus, that is not a sensible reading.

AFAIAA, in all those other cases the behaviour for null is made
explicitly defined. It certainly is for assert(), tmpnam(), free(),
realloc() and freopen(). I also think that "many" is a bit of an
over-statement. There are several; but for easily the majority of
functions taking pointers, a null pointer is not a valid option, surely?
No, that is not so. The validity of a function argument
generally depends on more than its type. For example,
strcpy of more data than there is room for is clearly an
instance of an invalid integer argument.

strcpy() has integer arguments?
Any void-pointer *value* that is valid up to that context
is clearly meant to be handled by the %p specifier. A
null pointer value, correctly obtained, is but one of
many valid void-pointer values. Why should more be said
about any of them in this context?

Because it is obviously not clear, from the current text, that this is
true. The purpose of a programming language standard should not be to
define the language for those privileged few who can read the minds of
the committee (generally because those minds reside in their own heads),
but to everybody who wants to either create or use an implementation of
that language.

Richard
 
D

Dave Vandervies

But Keith Thompson has already agreed that, in cases where a
null pointer value has *explicit* semantics (fflush, strtok,
etc), it's clearly valid and there's no problem.

It's worth noting that for all of these, the semantics explicitly stated
for a null pointer are different from those for non-null pointers.
In the
case of fprintf, however, the null pointer value is *not* given
explicit semantics.

fprintf is defined to format "%p" as the value of a pointer it's given.
A null pointer has a valid value to do this with, so the semantics for
a null pointer are the same as for any other valid pointer value, and
don't need to be stated explicitly.

Most functions that take pointers are defined as acting on what the
pointer points at; since null pointers are defined to not point at
anything, a null pointer is (obviously) an invalid value. This is
common enough (are there any library functions other than the fprintf
and fscanf families' handling of %p that act on pointer values and not
the objects the pointer points at?) that it's reasonable to use as an
example of invalid inputs to a function, but for functions that act on
the value of the pointer and not the object it points at, this restriction
doesn't apply.

This could be stated more clearly, to ensure that an actively hostile
interpretation would still have to reach the conclusion that it's
unambiguous, but I don't see any ambiguity in it.

If anything should be changed, it would be removing null pointers from the
list of examples of invalid inputs, since anywhere it's valid it would
already be covered under "a value outside the domain of the function".
But just because something appears in a list of examples doesn't mean
that it applies in every case where it could come up.


dave

--
Dave Vandervies (e-mail address removed)
Genghis Khan, Immanuel Kant.
In today's lesson, we learn that brute force is more productive than thinking.
--Chris Suslowicz and David P. Murphy in the scary devil monastery
 
D

Dave Vandervies

AFAIAA, in all those other cases the behaviour for null is made
explicitly defined. It certainly is for assert(),

Unless it's been changed between N869 and C99, the behavior for a null
pointer given to assert isn't explicitly defined; it defines an action
on a scalar value it's given without mentioning pointers at all.

If you're willing to extend the examples of invalid arguments to functions
given in 7.1.4#1 to apply to macros as well, does the existence of a
null pointer in that list mean that a null pointer is an invalid argument
to assert?

If not, how is assert's checking of a value different from printf's
formatting of it? Why is it valid for one library call that acts on
the value and not for another one that also acts on the value?

tmpnam(), free(),
realloc() and freopen(). I also think that "many" is a bit of an
over-statement. There are several; but for easily the majority of
functions taking pointers, a null pointer is not a valid option, surely?

A function that's defined to act on what a pointer points at, and not
defined to act appropriately when given a null pointer (which doesn't
point at anything), needs a pointer that points at something (which
excludes null pointers). A function that acts on the pointer's value,
rather than what it points at, doesn't have this restriction.


dave
 
B

Brian Inglis

Sure it is, since for many of the functions a null pointer
value has its own explicit semantics. With your reading,
those would all be invalid values, resulting in undefined
behavior, in which case it makes no sense for the behavior
to be specified. Thus, that is not a sensible reading.

AFAIAA, in all those other cases the behaviour for null is made
explicitly defined. It certainly is for assert(), tmpnam(), free(),
realloc() and freopen(). I also think that "many" is a bit of an
over-statement. There are several; but for easily the majority of
functions taking pointers, a null pointer is not a valid option, surely?
No, that is not so. The validity of a function argument
generally depends on more than its type. For example,
strcpy of more data than there is room for is clearly an
instance of an invalid integer argument.

strcpy() has integer arguments?
Any void-pointer *value* that is valid up to that context
is clearly meant to be handled by the %p specifier. A
null pointer value, correctly obtained, is but one of
many valid void-pointer values. Why should more be said
about any of them in this context?

Because it is obviously not clear, from the current text, that this is
true. The purpose of a programming language standard should not be to
define the language for those privileged few who can read the minds of
the committee (generally because those minds reside in their own heads),
but to everybody who wants to either create or use an implementation of
that language.[/QUOTE]

The standard does seem to lean towards providing adequate
documentation for implementers, presumably because of their
justifiable involvement in the process and committee, rather than
providing an unambiguously understandable specification for
programmers.

Programmers have to rely on the implementers for comprehensible
documentation.
So it does not surprise me that most programmers consider
implementation documentation as the gospel, as it meets their needs.
Nor does the conflicting viewpoints between standard participants, who
obviously understand what the standard means by what it says, and the
other posters to this group, who are confused by the ambiguous,
implicit, unstated, and undocumented assumptions behind the statements
in the standard.

If it was obvious what the standard actually means by what it says,
there would be little need for many of these discussions.
Considering the knowledge of the details in the standard by posters
not who were not participants, and that even they are unsure of what
the standard means in some places, there could perhaps be an informal
acknowledgement in this forum that some additional clarification may
be necessary for those who are neither implementers nor participants.
 
R

Richard Bos

Unless it's been changed between N869 and C99, the behavior for a null
pointer given to assert isn't explicitly defined; it defines an action
on a scalar value it's given without mentioning pointers at all.

True, but it does explicitly mention that this scalar is compared to 0.
This means that you can pass pointers that can compare equal to 0 to
assert(); that is, assert(NULL) is valid (and aborts the program).

Richard
 
P

pete

Dave Vandervies wrote:
Unless it's been changed between N869 and C99, the behavior for a null
pointer given to assert isn't explicitly defined; it defines an action
on a scalar value it's given without mentioning pointers at all.

Are you aware that pointer types are scalar types?
Your statement makes me think that you are not.

There are three major categories of types:
1 function types
2 incomplete types
3 object types

Old style function types are according to the return type
of the function. Modern function types also include the parameter
types. The only two things that can be done with the value
of an expression of a function type,
is that it can be converted to a pointer to a function of that type,
or it can be the operand of unary & operator, which returns a pointer.

In the expression
puts("Hello")
puts is converted to a pointer.

In the expression
(&puts)("Hello")
puts is not converted. The result of the & operator is a pointer
and that result is not converted by the function call.
The sizeof and unary & operators
never cause conversion of their operands.

In the expression
(*puts)("Hello")
puts is converted by the unary * operator to a pointer.
The result of the operation is an expression of a function type
and is converted to an expression of pointer type by
the function-call operator ().


The only two incomplete types that I'm aware of are
void
and ones of the form
array[]
sizeof is not defined for expressions with incomplete types.
Expressions of type void have no values.
The only three things that you can do with the value of
an array of unknown size is:
1 convert it to a pointer to it's first element
2 initialize it and convert it to an array of some specified size
3 it can an be the operand of unary &

Object types, in addition to objects, also apply to:
constant expressions,
5
indeterminate expressions,
array[-1]
and rvalues.
(a + b)

There are three major catagories of object types:
1 scalar types
2 agregate types
3 unions

There are two kinds of scalar types:
arithmetic types and pointer types.
The unary * operator is only defined for pointer types.
There are two kinds of arithmetic types:
floating types and integer types.
Bitwise operators are not defined for any types except integer types.

There are two kinds of aggregate types:
1 arrays
2 stuctures
The assignment operator is defined for expressions of structure type.
Structure members of array type, are byte copied when
the structure containing them is the right operand
of the assignment operator.
The equality and relational operators are not defined
for expressions of structure type.
The value of an expression of array type can be:
1 the operand of unary &
2 converted to a pointer to it's first element
3 used as an initializer
4 copied bytewise when it is a member of a structure which
is a right operand of the assignment operator.

And that's all that I have to say about types for now.
 
D

Dave Vandervies

Are you aware that pointer types are scalar types?

I am.
Your statement makes me think that you are not.

Claiming that an action defined on values for all scalar types is
invalid for a particular value of a particular class of scalar types
(as you appear to think I'm doing here) isn't all that different from
claiming that an action defined on values for a particular scalar type
is invalid for a particular value of that scalar type (which is what
started this whole discussion).

The claim I was replying directly to was that assert explicitly defines
action for a null pointer; if defining it as comparing a scalar value
to 0 is explicitly defining an action on a null pointer (which I don't
think it is, but I also don't think it's necessary to do so), then
defining fprintf's %p as formatting the value of a void pointer value
should also be considered an explicit definition of an action on a null
pointer (because a null pointer has just as valid a value as any other
pointer until you try to dereference it).

If assert's definition of acting on values of scalar types supersedes
the inclusion of null pointers in a list of examples of invalid inputs
(which I think we all agree on), doesn't fprintf's definition of acting
on values of pointers-to-void supersede the same inclusion (which there
seems to be disagreement on), by the same reasoning?


dave
 
D

Dave Vandervies

True, but it does explicitly mention that this scalar is compared to 0.
This means that you can pass pointers that can compare equal to 0 to
assert(); that is, assert(NULL) is valid (and aborts the program).

I don't disagree with this.

I do disagree with the claim that similar reasoning doesn't apply to
fprintf's %p format specifier, which was the context of my statement
above. The definition of fprintf explicitly states that the value of the
pointer it's given is converted to a sequence of printing characters;
this applies just as much to a null pointer as to any other valid
pointer value.

I don't see how it's reasonable to apply this reasoning to assert (which
there doesn't seem to be any disagreement on) but not to fprintf.


dave
 
R

Richard Bos

I don't disagree with this.

I do disagree with the claim that similar reasoning doesn't apply to
fprintf's %p format specifier, which was the context of my statement
above.

The definition of assert() explicitly mentions 0 values. The definition
of printf("%p...) does not, and is AFAICT unique in this.

Richard
 
P

pete

Richard said:
(e-mail address removed) (Dave Vandervies) wrote:

The definition of assert() explicitly mentions 0 values.
The definition
of printf("%p...) does not, and is AFAICT unique in this.

In all of the rest of the standard functions
where NULL is an invalid argument, the standard's description
of the function will define arguments in such a way
so as to exclude NULL, unlike the way printf is described.

When the standard says "the object pointed to by s1" or
"the initial element of which is pointed to by base"
then those words exclude NULL as a valid value for both s1 and base.

All of the library descriptions are like that.
 
D

Douglas A. Gwyn

infobahn said:
But Keith Thompson has already agreed that, in cases where a
null pointer value has *explicit* semantics (fflush, strtok,
etc), it's clearly valid and there's no problem.

That counter-argument relies on "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 ..., the behavior is
undefined." Certainly if you examine the fprintf spec,
a null pointer value is not inherently invalid in that
context; that pointer argument is not required to point
at an object (unlike e.g. the arguments for strcpy).
You don't even need to refer to the parenthetical clause
("..." above), which gave examples of classes of values
that might be considered invalid, generally depending on
context. Not every use of a null pointer is invalid.
 
D

Douglas A. Gwyn

Keith said:
I've been wondering why the standard says that the value of a free()d
pointer becomes indeterminate and not that it becomes a trap
representation; ...

"Trap representation" was a C99 invention (mainly by Clive
Feather, during revision of the specs for integer reps),
and was meant to cover bit patterns that were not valid
values. Since in the case of a freed pointer it is not
the bit pattern but rather its *meaning* that may have
become indeterminate, "trap rep" didn't seem appropriate.
 
D

Douglas A. Gwyn

Trevor L. Jackson said:
I.e., if there were no intervening heap oprtations the most recently
free()'d block was still available *and it's contents unchanged*.
Clearly that is no longer true.

That hasn't been true since C was standardized, and wasn't a
property to rely on even before that.
 
D

Douglas A. Gwyn

Trevor L. Jackson said:
So realloc() is explicitly defined to not handle stale pointers?

How would you pass the pointer value as an argument without
evaluating it?
 
K

Keith Thompson

pete said:
In all of the rest of the standard functions
where NULL is an invalid argument, the standard's description
of the function will define arguments in such a way
so as to exclude NULL, unlike the way printf is described.

When the standard says "the object pointed to by s1" or
"the initial element of which is pointed to by base"
then those words exclude NULL as a valid value for both s1 and base.

All of the library descriptions are like that.

If the behavior of printf("%p\n", (void*)0) depends on the way all the
other standard functions are described, I think it would be better to
describe the behavior of the "%p" format specifier.

I think I'd even be happy with a footnote.
 
J

James Kuyper

Keith said:
If the behavior of printf("%p\n", (void*)0) depends on the way all the
other standard functions are described, I think it would be better to
describe the behavior of the "%p" format specifier.

That's not how I understood Keith's argument. He's arguing that a single
consistent approach is used: a null pointer argument is invalid whenever
the corresponding description of the behavior implies that the argument
is dereferenced. He's referring to the other descriptions only as
confirmation of this convention, not as something that must be read in
order to understand the description of printf().
 
J

James Kuyper

James said:
....
That's not how I understood Keith's argument. He's arguing that a single
pete's

Sorry! I was paying more attention to arguments than to attributions.
 

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,770
Messages
2,569,588
Members
45,093
Latest member
Vinaykumarnevatia00

Latest Threads

Top