"a < b < c" not the same as "(a < b) && (b < c)"?

M

Mabden

Keith Thompson said:
Mabden wrote: [...]
I still have a question as to whether an automatic pointer (in a
function, initialized by the compiler) is set to all-bits-zero or to the
address of NULL (void *(0)). If pointers are init'ed to zero as other
automatic variables are in a function, and NULL points to somewhere else
in memory aren't they different?

I think he was actually asking about the implicit initialization of
automatic variables. The answer, of course, is that there is none.

I was actually asking about whichever pointers are initialized to
something by the compiler, and whether that something was all-bits-zero
or NULL. Kuyper says they init'ed to NULL, but that there may be more
than one NULL (which I had never heard of before). Of course we all know
NULL may or may not be at location zero, so let's not revisit that
again.
To expand on that a little:

The current value of a pointer object is the value most recently
assigned to it, or the value with which it was initialized if it's
never been assigned to. (I'm assuming "assigned to" refers to any
method of modifying the value, including memcpy(); I'm also ignoring
any issues involving volatile objects.) The same is true of objects
of any type; there's nothing special about pointers here.

If the current value of a pointer object is a null pointer, assigning
it to a _Bool object assigns the value 0 (false). If the current
value is anything other than a null pointer, assigning it to a _Bool
object assigns the value 1 (true). If the pointer object is
uninitialized, which can happen if it's an automatic variable with no
explicit initialization, then any attempt to refer to its value
invokes undefined behavior; attempting to assign this uninitialized
value to a _Bool object can do anything. (It's likely to assign
either 0 or 1, but it could, for example, crash the program as soon as
the pointer value is accessed.) This can and should be avoided by
consistently initializing all variables (explicitly or implicitly)
before using them.

So it appears it is not so random as it first looked to me. I don't take
the uninititialized case into much account, since I initialize things at
all times, and I would consider that case a programmer error.

I was worried about moving working code to another platform, or even a
new version of a compiler, and having it break. Mainly cases like a
pointer set to all-zeroes being seen as a false in the case where it is
a valid location for some piece of hardware, and should be a true.

That said, I still don't see a very compelling reason to store the true
/ false value of a pointer, rather than just testing the pointer itself.
Therefore, if I may refer back to your comment that started this bit of
the thread, I find setting a _Bool to a pointer "uninteresting".

I apologize to all that this thread went on so long, but sometimes these
fine points can't be easily explained, and I find that I must be
explaining my point in Martian or something, sometimes. (Well, being an
offensive bastard probably doesn't encourage people in wanting to help
me out, either)

I hope we've resolved this. I am satisfied, at least.
 
K

kuyper

Mabden said:
....
Yes, that is what I meant. If the pointer is set to a value. You use
static, someone else pointed out I had it backwards, and the it was
globals that are zeroed.

All global variables have static storage duration, but local variables
can also be static if explicitly declared so. The same initialzation
rules apply in either case.
THAT was my question. I was asking if an all-bits-zero address that is
assigned to a _Bool, would essentially be a random operation, since some
machines may put NULL at all-bits-zero and some may not; maybe not so
much random, but a hiding bug. You are saying that that should not
happen.

You still aren't wording that quite right, so I'm not sure whether
you're understanding it correctly. It's not random; it's implementation
defined. To reiterate:

A pointer with all bits zero might or might not be a null pointer,
depending upon the implementation; it might not even be a valid
pointer.
A pointer that refers to the memory address 0 might or might not be
null, and might or might not have all bits set to zero, and might or
might not be valid.
NULL, like all other null pointer constants, when converted to a
pointer type, always converts to a null pointer value.

However you created it, a null pointer always converts to a _Bool as
false. All non-null pointers convert as true. Therefore, if you need
the construct to be portable, initialize your pointer with a null
pointer constant, such as NULL, 0, (void*)0, (3-3), '\0', 0x0, 0L, 0U,
12345*0, 0/2, ~UINT_MAX, or whatever other form takes your fancy.
 
K

kuyper

Mabden said:
....
I was actually asking about whichever pointers are initialized to
something by the compiler, and whether that something was all-bits-zero
or NULL. Kuyper says they init'ed to NULL, but that there may be more
than one NULL (which I had never heard of before). Of course we all know
NULL may or may not be at location zero, so let's not revisit that
again.

Actually, I said that static pointers that are not explicitly
initialized get zero-initialized. The result is a null pointer, which
is also what you would get if you explicitly initialized it with NULL,
but that's not the same as saying that they were initialized with NULL.

....
I was worried about moving working code to another platform, or even a
new version of a compiler, and having it break. Mainly cases like a
pointer set to all-zeroes being seen as a false in the case where it is
a valid location for some piece of hardware, and should be a true.

That's a reasonable concern. There's no gurantee that a pointer with
all bits zero is even a valid pointer, much less that it is a null
pointer. Code that assumes that it is needs to be re-written.
That said, I still don't see a very compelling reason to store the true
/ false value of a pointer, rather than just testing the pointer itself.
Therefore, if I may refer back to your comment that started this bit of
the thread, I find setting a _Bool to a pointer "uninteresting".

The resulting _Bool records whether or not the corresponding pointer
was non-null. I don't see a great deal of use for such a variable, but
neither does it sound completely unreasonable to me.
 
P

pete

Mabden wrote:

You still aren't wording that quite right, so I'm not sure whether
you're understanding it correctly. It's not random;
it's implementation defined.

I don't think that implementations are required to
document what an all bits zero address is.

After:
void *pointer = memset(&pointer, 0, sizeof pointer);
(pointer == NULL) is undefined.
The value of pointer is just simply undefined.
 
K

Keith Thompson

Mabden said:
I was actually asking about whichever pointers are initialized to
something by the compiler, and whether that something was all-bits-zero
or NULL. Kuyper says they init'ed to NULL, but that there may be more
than one NULL (which I had never heard of before). Of course we all know
NULL may or may not be at location zero, so let's not revisit that
again.

You keep using the term "NULL" when you're really talking about null
pointer values. They're two very different (but related) things, and
using NULL as a shorthand for a null pointer value is potentially
misleading.

NULL is a macro defined in <stddef.h> and a few other standard
headers. It expands to a null pointer constant. A null pointer
constant is an integer constant expression with the value 0, or such a
constant cast to type void*. It is a construct that occurs only in
source code; there are no null pointer constants at execution time.

The result of converting a null pointer constant to a pointer type is
a null pointer. A null pointer is a value that exist at execution
time. A null pointer constant can *denote* a null pointer value, but
it's not the same thing -- just as character sequence "42" (a decimal
digit '4' followed by a decimal digit '2') appearing in foo.c
represents, but is not the same as, the 0000000000100101 bit pattern
that might appear in memory at execution time. The run-time
representation of a null pointer value is arbitrary; the compiler's
job may be a bit simpler if it happens to be all-bits-zero, but that
should be of no concern to programmers.

When you say that "there may be more than one NULL", you could be
talking about any of a number of entirely different things.

The NULL macro is defined in several standard headers. It's most
likely defined in the same way for a given implementation (there's no
good reason for them to differ).

There are several possible ways to define the NULL macro; this will
vary from one implementation to another. For example each of the
following is valid:

#define NULL 0

#define NULL ((void*)0)

enum { __null };
#define NULL __null

#define NULL (42-6*7)

On an entirely unrelated note, it's possible to have more than one
representation for a null pointer. The representation of a null
pointer can vary from one pointer type to another -- for that matter,
the representation of a pointer itself can vary from one pointer type
to another. Or there can be two or more distinct representations that
are treated as null pointers; in that case, the "==" operator on
pointers must treat these representations as equal to each other.
There's seldom any good reason for an implementation to go through
such contortions, but the standard allows a great deal of latitude.
(There is no relationship between the multiple possible definitions of
the NULL macro and the multiple possible run-time representations of a
null pointer value.)

Any such variations should not affect well-written code. As long as
you don't assume (as some bad code does) that pointers are really
integers, you should be ok. The one thing you have to be careful
about is that there are some operations that initialize objects to
all-bits-zero, such as memset() and calloc(); these do not reliably
set pointers to null pointer values, or floating-point objects to 0.0.
On the other hand, the default initialization of static objects sets
the object to the appropriate zero value for its type, whether that's
'\0', 0, 0.0, or a null pointer value, however that value happens to
be represented. (Once again, the use of the term "zero value" for a
null pointer implies nothing about its representation.)

All of this is explained in the standard. You can find a committee
draft at <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf>;
it includes the C99 standard plus some later corrections (marked with
change bars). Perhaps if you downloaded a copy, you could avoid
wasting a great deal of everyone's time.

[...]
That said, I still don't see a very compelling reason to store the true
/ false value of a pointer, rather than just testing the pointer itself.
Therefore, if I may refer back to your comment that started this bit of
the thread, I find setting a _Bool to a pointer "uninteresting".

There may not be a compelling reason to use the feature, but there is
a good reason to define it that way. For any expression, the following:

_Bool b = <expr>;

is equivalent to:

_Bool b = (<expr> ? 1 : 0);

or, with #include <stdbool.h>:

bool b = (expr ? true : false);

The expressions and types that can be assigned to a _Bool object are
exactly the same as the expressions and types that can be used in a
condition. (I *think* that's correct; I'm sure someone will correct
me if it's not.)
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top