NULL with representation other then all bits 0

G

Gordon Burditt

As a real life example, consider a magnetic core memory.
This could be an interesting setup. Assuming you can have more
than one bank of this memory, it could be made into a number
represented as bit fields consisting of (ABA routing number, plane,
y, x). An integer might consist of 7 bytes, and relative to the
pointer to the integer, you have the bytes at addresses (plane, y,
x):

(0,0,0) (+1,0,0) (-1,0,0) (0,-1,0) (0,+1,0) (0,0,+1) (0,0,-1)
On the other hand, an *unsigned* integer might have bytes at addresses:
(0,0,0) (+1,0,0) (+2,0,0) (+3,0,0) (0,-1,0) (0,-2,0) (0,-3,0)

Now, trying to allocate stuff in memory becomes sort of a 3-dimensional
Tetris puzzle. Any use of the concept "contiguous" gets pretty
much thrown out the window. And trying to use allocated stuff with
malloc() becomes darn near impossible if malloc() allocates a 1-D
memory vector.
That core memory module you describe is indeed interesting. If I had one
and wanted to address it in a C program with a pointer, how would I go
about it? What object type would the pointer have?

Just about any pointer can be represented as a bunch of bits, optionally
divided into bit fields to represent various stuff (segment number,
ring number, offset, protection level, global/local, etc.)
Would I need a special compiler that knows about the module? How would
the innocent C programmer deal with plane coordinates and segment id's
of this subsystem? Or you're just teasing me?

The implementation I described makes functions like malloc() pretty
much hopeless (and therefore trying to put C on it is teasing you).
The C++ new operator knows the type, so it could allocate wierd
shapes of memory needed to handle funky objects.

Gordon L. Burditt
 
C

Christian Bau

Is that really true if
((void *)0xffffffff) == 0
which an implementation can ensure?

0xffffffff still doesn't have a value of zero.

Take a more concrete example: On many existing applications, you will
find that


((void *) 0xffffffff00000000) == 0

0xffffffff00000000 is a constant of type unsigned long long, with the
lowest 32 bits equal to zero. On many implementations with 32 bit
pointers, this constant cast to a pointer type will produce a null
pointer. But 0xffffffff00000000 is not a null pointer constant.
 
C

Christian Bau

Keith Thompson said:
Not quite. It only has to make sure that a conversion (explicit or
implicit, not just a cast) of an integer *constant* 0 to a pointer
type produces a null pointer. There is no requirement that conversion
of a non-constant zero has to yield a null pointer, or that conversion
of a null pointer value has to yield an integer zero.

For example:

char *null_ptr = 0; /* guaranteed to be a null pointer value */
int zero = 0;
char *maybe_null = (char*)zero; /* may or may not be a null pointer */

The requirements for null pointer constants apply only to compile-time
constructs.

I believe this has been added as a requirement in C99.
 
C

Christian Bau

Joe Wright said:
1. What are you drinking?
2. Can I have some?

Seems you had some already. Seems you had plenty too much already. Take
a copy of the C Standard, read it carefully, then read my post again.
 
C

Christian Bau

CBFalconer said:
Within sharp limits.


Within even sharper limits.


and this operation is very heavily restricted to pointers to
locations within the same object. Otherwise meaningless.


Not so. A pointer only has to point. It can do this in any way it
likes, including specifying the phase of the moon when dereferenced
or the need for sacrificial virgins.

As a real life example, consider a magnetic core memory.
Addressing is in terms of x, y, and plane coordinates. This is not
a number. It is a tuple. In modern systems it may be made up of
process id, segment id, page id, page offset, and bit or byte.

Just take an old x86 implementation with segments and offsets. The null
pointer is represented by a null segment with offset 0. All other
pointers are represented by valid segments (and segments other than the
null segment are most definitely not all valid! ) and an offset.

This doesn't correspond to memory addresses at all; memory addresses can
actually change due to demand paging, where the complete memory
addressed by one segment can be swapped to the harddisk and can be
swapped in later at a different memory location.

Adding any number to a null pointer on such an implementation will
never, ever result in a valid pointer.
 
J

Joe Wright

Keith said:
Joe Wright said:
Keith Thompson wrote:
[...]
You've read sections 4 and 5 of the comp.lang.c FAQ, right?

Sure, lots of times. Even as Chief Pedant, talking down doesn't win points.


No offense intended. Come to think of it, I don't think I've read
those entire sections myself since they were updated; I probably
should.
No offence taken. Peace. I really enjoy C. I began in 1988 if I recall
correctly with a CP/M compiler from Software Toolworks and a tattered
copy of K&R1. I still have that copy of the Old Testament. I got
interested again in the 90's and followed X3J11 as closely as I could.
The C89 result was good. Especially prototypes and therefore implicit
conversions. I think I have a reasonable handle on how C works.

Dennis Ritchie are Brian Kernighan are heroes of mine. I am not
particularly enticed by or enamoured of C99. Neither here nor there.
 
C

Christian Bau

Joe Wright said:
You clearly (from the above paragraph) do know what I mean. I'm sorry if
I was confusing. But further if I might,

char *p = malloc(100);
if (p == NULL)

The test is valid because malloc will return NULL or a valid pointer (OK
I said pointer, not address). But if then..

malloc will not return NULL. It may return a null pointer. A null
pointer is not the same as NULL. NULL is a null pointer constant, but it
can be an int, or a long, or an unsigned long etc.

free(p);
...
if (p == NULL)

This test is invalid because p is now indeterminate. Right?

Completely irrelevant to this discussion.
 
K

Keith Thompson

Christian Bau said:
I believe this has been added as a requirement in C99.

What has? Do you mean that maybe_null in the example is required to
be a null pointer under C99?

I don't believe that's correct. In particular, I don't believe the
requirements have changed significantly between C90 and C99. (Of
course I've been wrong before.) Can you provide chapter and verse?
 
K

Keith Thompson

Christian Bau said:
malloc will not return NULL. It may return a null pointer. A null
pointer is not the same as NULL. NULL is a null pointer constant, but it
can be an int, or a long, or an unsigned long etc.

As long as we're being picky, NULL isn't a null pointer constant; it's
a macro that expands to a null pointer constant. If I were going to
be even pickier, I might say that NULL is a sequence of four
characters that, taken together in an appropriate context in a C
source file, form a preprocessing token that happens to be a macro
name that is expanded to a null pointer constant.

Similarly, given
int x = 42;
I might say that x has the value 42, even though 42 is an integer
constant (or a sequence of two characters), not an integer value.
(The trouble there is that we don't have a good name for the value
other than 42.)

The whole point of using a symbol (a word, an identifier, a mark on a
clay tablet, a meaningful grunt) is that the symbol is a shorthand for
the thing it represents. In this case, NULL is fairly clearly meant
to be a shorthand for the thing that it represents, namely a null
pointer value.

Having said all that, I agree that NULL is not a very *good*
conversational shorthand for "null pointer value", partly because it
makes more sense as a shorthand for a null pointer constant than for a
null pointer value (a very important distinction) and partly because
we have an unambiguous term for a null pointer value, namely "null
pointer value". But I think a brief clarification of that point would
be sufficient when, as in this case, the intent is reasonably clear.
 
K

Keith Thompson

Keith Thompson said:
As long as we're being picky, NULL isn't a null pointer constant; it's
a macro that expands to a null pointer constant. If I were going to
be even pickier, I might say that NULL is a sequence of four
characters that, taken together in an appropriate context in a C
source file, form a preprocessing token that happens to be a macro
name that is expanded to a null pointer constant.
[snip]

Yeah, that was pedantic, even for me. :cool:}
 
J

Joe Wright

Christian said:
Seems you had some already. Seems you had plenty too much already. Take
a copy of the C Standard, read it carefully, then read my post again.

I sincerely apologize for the drinking crack. I thought it would be
funny. It was not. Again, I apologize.

I was probably reacting to your condescending attitude. No biggie.
 
R

Rod Pemberton

Keith Thompson said:
As long as we're being picky, NULL isn't a null pointer constant; it's
a macro that expands to a null pointer constant. If I were going to
be even pickier, I might say that NULL is a sequence of four
characters that, taken together in an appropriate context in a C
source file, form a preprocessing token that happens to be a macro
name that is expanded to a null pointer constant.

Actually, if you want to be _really_ picky, it, apparently, doesn't have to
expand to _anything_ as long as the compiler has an internal representation
for it. If you pull the code from GCC 4.0.2 and GLIBC 2.3.6 (current
versions), you'll see the compiler creates a special node for NULL in the
code generator. As far as I can tell, it appears that node is given special
attributes, and no value is ever inserted into the node. (But, I could be
wrong. It is quite complex... )

#define NULL (__null)

__null triggers the special code.


Rod Pemberton
 
C

CBFalconer

Joe said:
CBFalconer wrote:
.... snip ...

That core memory module you describe is indeed interesting. If I had
one and wanted to address it in a C program with a pointer, how
would I go about it? What object type would the pointer have?

Would I need a special compiler that knows about the module? How
would the innocent C programmer deal with plane coordinates and
segment id's of this subsystem? Or you're just teasing me?

The C programmer, innocent or guilty, need not know anything about
the mundane details of the pointer guts. The system designer, on
the other hand, has to worry about it all. That is why a cast
between integral values and pointers may occur, but the result may
be meaningless.

--
"The power of the Executive to cast a man into prison without
formulating any charge known to the law, and particularly to
deny him the judgement of his peers, is in the highest degree
odious and is the foundation of all totalitarian government
whether Nazi or Communist." -- W. Churchill, Nov 21, 1943
 
J

Jordan Abel

Actually, if you want to be _really_ picky, it, apparently, doesn't have to
expand to _anything_ as long as the compiler has an internal representation
for it.

You have to be able to stringize it, it's a macro.
 
C

Christian Bau

Keith Thompson said:
What has? Do you mean that maybe_null in the example is required to
be a null pointer under C99?

I don't believe that's correct. In particular, I don't believe the
requirements have changed significantly between C90 and C99. (Of
course I've been wrong before.) Can you provide chapter and verse?

From the n1124 draft:

6.3.2.3 Pointers:

3. An integer 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 converted to a pointer type, the resulting
pointer, called a null pointer, is guaranteed to compare unequal
to a pointer to anyobject or function.

5. An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be correctly
aligned, might not point to an entity of the referenced type, and might
be a trap representation.

6.3.2.3.3 only talks about conversions, it doesn't talk about when
conversions happen (which has a special case for null pointer
constants). It says what the result of a conversion from an integer type
to a pointer type is in one special case. Note that we have here a full
conversion, and not a substitution.

I assume that the result of a conversion can only depend on the value
and the type of the thing that is converted, which implies that anything
that has a type and a value that could be the type and value of a null
pointer constant _must_ be converted to a null pointer. (I don't think
it is possible to have a null pointer constant of type char or short, so
converting a char or short of value 0 to pointer type might not be
guaranteed to produce a null pointer with this reasoning).

As an example, in

char* p = (char *) 0;

the special rules for null pointer constants are not invoked;

char* p = (char *) 1;

would be just as legal; it is just a conversion from int to char*. The C
Standard defines the result in the very strict case we have here, that a
null pointer constant is converted, but the result of a conversion
cannot depend on this fact. Zeroes that are not null pointer constants
are not mentioned in the rule, but they cannot be converted differently.

The result is implementation defined; an implementation would not be
free to define different results for

(char *) 1
(char *) (1 + 0)
(char *) (int) (1.73f)

because they all convert the same value. Neither can it define different
results for different zeroes.
 
K

Keith Thompson

Christian Bau said:
From the n1124 draft:

6.3.2.3 Pointers:

3. An integer 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 converted to a pointer type, the resulting
pointer, called a null pointer, is guaranteed to compare unequal
to a pointer to anyobject or function.

5. An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be correctly
aligned, might not point to an entity of the referenced type, and might
be a trap representation.

6.3.2.3.3 only talks about conversions, it doesn't talk about when
conversions happen (which has a special case for null pointer
constants). It says what the result of a conversion from an integer type
to a pointer type is in one special case. Note that we have here a full
conversion, and not a substitution.

I assume that the result of a conversion can only depend on the value
and the type of the thing that is converted, which implies that anything
that has a type and a value that could be the type and value of a null
pointer constant _must_ be converted to a null pointer. (I don't think
it is possible to have a null pointer constant of type char or short, so
converting a char or short of value 0 to pointer type might not be
guaranteed to produce a null pointer with this reasoning).

As an example, in

char* p = (char *) 0;

the special rules for null pointer constants are not invoked;

char* p = (char *) 1;

would be just as legal; it is just a conversion from int to char*. The C
Standard defines the result in the very strict case we have here, that a
null pointer constant is converted, but the result of a conversion
cannot depend on this fact. Zeroes that are not null pointer constants
are not mentioned in the rule, but they cannot be converted differently.

The result is implementation defined; an implementation would not be
free to define different results for

(char *) 1
(char *) (1 + 0)
(char *) (int) (1.73f)

because they all convert the same value. Neither can it define different
results for different zeroes.

To summarize your argument:

(1) The standard guarantees that an integer constant expression with
the value 0, when converted to a pointer type, yields a null
pointer value.

(2) The result of a conversion depends only on the value and type of
the operand.

(3) Therefore, the integer value 0, when converted to a pointer type,
yields a null pointer value.

I'm not convinced that (2) is necessarily required. (It's going to be
true in the majority of real-world implementations, on which null
pointers are represented as all-bits-zero, but those aren't the cases
we're worried about.)

The conversion of an integer constant expression is a special case.
If it had been intended for the conversion of a non-constant zero to
yield a null pointer, the standard could have said so, and it would
have been simpler than the current wording.

In particular, I believe the following could be a conforming
implementation:

Pointers are 32 bits. A null pointer is represented uniquely as
0xffffffff. A pointer with the representation 0x00000000 is not a
null pointer. Conversion of an integer constant expression with the
value zero to a pointer type yields a null pointer value (as required
by the standard). Conversion of a non-constant integer expression to
a pointer type yields a pointer value with the same representation as
the integer (possibly zero-extended or truncated).

For example:

void *null_ptr = 0; /* obviously a null pointer */
void *null_ptr2 = (void*)0xffffffff; /* a null pointer */
int zero = 0;
void *zero_ptr = (void*)zero; /* not a null pointer */

null_ptr2's value is a null pointer, but (void*)0xffffffff is not a
null pointer constant.

I admit that having (void*)0 and (void*)zero yield different values is
counterintuitive, but so is the whole idea of C's null pointer
constants IMHO. C's definition of a null pointer constant is so
narrow that I think it has to allow for this possibility.
 
G

Gordon Burditt

#define NULL ((void *)0xFFFFFFFF), assuming that that is in fact a null
Gordon, *please* don't snip attribution lines. In following a
discussion, it's important to know who said what.

It is more important not to misattribute someone's words to someone else.
Your headers
indicate that you're using the "trn" newsreader; I'm reasonably sure
it does attributions correctly.

If I don't snip attributions, I get complaints of the form "I didn't
say that" from at least two people complaining that I attributed
the SAME text to each of them. On *EVERY* *SINGLE* *POST*. Even
if there was only one attribution left in. And some of them threaten
lawsuits. If attributions are supposed to indicate who said what,
it's not working, as clearly not everyone interprets them the same
way.

attribution -> misattribution

Note that it's completely irrelevant as to whether *I* can interpret
attributions correctly. I think I can, but I doubt anyone else
cares. And I think what was said is much more important than who
said it, at least most of the time.

Yes, that's really true. 0xFFFFFFFF doesn't have a value of zero, so
neither 0xFFFFFFFF nor (void*)0xFFFFFFFF is a null pointer constant
within the standard's definition of the term -- even if converting
0xffffffff to void* happens to yield a null pointer value.

A null pointer constant is a specific source construct, not just any
constant expression that happens to evaluate to a null pointer value.

Ok, so it's source, not runtime.
Having said that, an implementation can probably make it a null
pointer constant *as a documented extension* -- but there's little
point in doing so. There are far too many valid null pointer
constants already; we don't need any more.

But at runtime, you'd have to have all null pointer values compare
equal to all other null pointer values, right? So if on a particular
implementation, a segment number of 0 indicates a null pointer,
regardless of the offset, protection ring, or assorted other bits
making up a pointer, the code that compares pointers would have to
deal with this?

Gordon l. Burditt
 
W

Walter Roberson

But at runtime, you'd have to have all null pointer values compare
equal to all other null pointer values, right? So if on a particular
implementation, a segment number of 0 indicates a null pointer,
regardless of the offset, protection ring, or assorted other bits
making up a pointer, the code that compares pointers would have to
deal with this?

Yes, for any given object type, two null pointers are guaranteed
to compare equal, even if they do not have the same internal
representation.

This is not -necessarily- as bad as the code that compares pointers
needing to iterate through all the different null pointer representations.
The compiler knows that pointer types at the time of the
comparison, so if some of the null pointer representations are not
possible for a given type, then the compiler would not need to
test that possibility. In particular, if there happens to be
exactly one null pointer representation per type (but that different
types might have different null pointer representations) then only
one test would need to be made.

This comparison process is potentially further simplified by the
fact that one cannot convert pointers directly between
incompatible types without going through void* [if I recall correctly].
The conversion into void* could produce a "canonical" null pointer
and the conversion of the "canonical" null pointer into a different
pointer type would choose one of the null pointer representations
valid for that type; in this way, one only has to deal with the
null pointer representations valid within the type. One thing to
watch out for here is that when a pointer is converted to void* and
converted back to the same type, the original pointer and the
double-converted one must compare equal -- but in the end this just
comes down to potentially needing to be able to compare multiple null
pointer representations for a single type.


If I were implementing a system with multiple null pointers, I
suspect I would, for each pointer comparison, insert type-specific
code for each of the pointers, that detected the various null pointer
possibilities and converted into a canonical null pointer. This would
give you a comparison that was linear in the number of different
null pointer representations, rather than one that was proportional
to the square of the number of representations.
 
F

Flash Gordon

Gordon said:
It is more important not to misattribute someone's words to someone else.


If I don't snip attributions, I get complaints of the form "I didn't
say that" from at least two people complaining that I attributed
the SAME text to each of them. On *EVERY* *SINGLE* *POST*. Even
if there was only one attribution left in. And some of them threaten
lawsuits. If attributions are supposed to indicate who said what,
it's not working, as clearly not everyone interprets them the same
way.

attribution -> misattribution

It may be the case on some groups you frequent, but seeing as most of
the posts I see here and else where have attributions and don't generate
such complaints this suggests to me that either:
1) When you leave in attributions you do them in a non-standard way
2) You have a lot of dealings on a group where people don't understand
the conventional way attributions work.
Note that it's completely irrelevant as to whether *I* can interpret
attributions correctly. I think I can, but I doubt anyone else
cares. And I think what was said is much more important than who
said it, at least most of the time.

I also care about who said what and find it annoying when a post does
not have proper attributions. I strongly suspect others here feel the
same way.
Ok, so it's source, not runtime.
Yes.


But at runtime, you'd have to have all null pointer values compare
equal to all other null pointer values, right? So if on a particular
implementation, a segment number of 0 indicates a null pointer,
regardless of the offset, protection ring, or assorted other bits
making up a pointer, the code that compares pointers would have to
deal with this?

Yes, or ensure that valid C code only generates one form of null
pointer. So it is exactly the same as the situation where you can have
both +ve and -ve zeros, either only one type must be generated or they
must compare equal.
 
K

Keith Thompson

It is more important not to misattribute someone's words to someone else.

Agreed, but ...
If I don't snip attributions, I get complaints of the form "I didn't
say that" from at least two people complaining that I attributed
the SAME text to each of them. On *EVERY* *SINGLE* *POST*. Even
if there was only one attribution left in. And some of them threaten
lawsuits. If attributions are supposed to indicate who said what,
it's not working, as clearly not everyone interprets them the same
way.

I vaguely recall some problems (possibly yours?) in which an
attribution line was left in with no corresonding quoted text, so that
the attribution appeared to be incorrect *unless* the reader carefully
counted the '>' characters.

I've never, or rarely, had that problem. Here's what I do:

1. Snip any quoted text that's irrelevant to the followup.

2. For each attribution, if the attribution line itself is preceded by
exactly N '>' characters, delete it if there's no remaining quoted
text preceded by exactly N+1 '>' characters.

It's not difficult. If you do that, you shouldn't get any complaints.
If you continue to drop attribution lines, you *will* get complaints.
attribution -> misattribution

Note that it's completely irrelevant as to whether *I* can interpret
attributions correctly. I think I can, but I doubt anyone else
cares. And I think what was said is much more important than who
said it, at least most of the time.

I agree that what was said is more important than who said it -- but
who said it *is* somewhat important.
Ok, so it's source, not runtime.


But at runtime, you'd have to have all null pointer values compare
equal to all other null pointer values, right? So if on a particular
implementation, a segment number of 0 indicates a null pointer,
regardless of the offset, protection ring, or assorted other bits
making up a pointer, the code that compares pointers would have to
deal with this?

We're talking about two different things. Having multiple null
pointer values (at runtime) calls for some extra work by the compiler,
such as in the code that does pointer comparison, but it shouldn't be
a problem for the programmer. Having multiple forms of *null pointer
constant* (0, 0UL, '\0', (void*)('-'-'-')) is required by the
language, and is a source of confusion; an implementation may be
allowed to define additional forms, but there's little benefit in
doing so, and certainly no benefit for anyone writing portable code.

Actually, that's not quite true. An implementation could have

enum { __null = 0 };
#define NULL __null

to make any use of the NULL macro recognizable after preproecessing;
it could then use this as a way to issue warnings (non-required
diagnoistics) for any use of NULL in a non-pointer context. (That
doesn't even require a language extension.)

But treating 0xffffffff as a null pointer constant just because
0xffffffff happens to be a representation of a null pointer value
would be largely pointless.
 

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,780
Messages
2,569,611
Members
45,273
Latest member
DamonShoem

Latest Threads

Top