[comp.lang.c++] references to dereferenced null pointers

jimad@microsoft.UUCP (Jim ADCOCK) (02/28/90)

/********

Is the following program legal, illegal, or undefined?  

[It compiles and runs "correctly" under cfront 2.0]

The program deals with the issue of the reference equivalent to a null
pointer -- ie what one would get if one could dereference a null pointer
and asssign the result to a reference.  As the following shows, at least
under cfront 2.0, the dereference followed by assignment to a reference is
a conceptual dereference only  --not actually done in machine code--  and
thus causes no null-pointer fault.  While the following example is weak,
I claim assigning a "null" to a reference is useful when defining 
iterators returning references rather than pointers.

[I don't believe this issue is covered in the C++ references]

********/

class OW
{
    char ow[4];
public:
    OW(char* p) { strncpy(ow, p, 4); }
    void print() { printf("%s ", ow); }
};

OW how("how"); OW now("now"); OW bow("bow"); OW wow("wow");
OW* pow[] = { &how, &now, &bow, &wow, 0 };

int main()
{    
    int i = 0;
    while (1)
        {
        OW& row = *pow[i++];
        if (!&row) break;
        row.print();
        }
    return 0;
};

rfg@ics.uci.edu (Ronald Guilmette) (02/28/90)

In article <51083@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>/********
>
>Is the following program legal, illegal, or undefined?
>
>[It compiles and runs "correctly" under cfront 2.0]

Not surprizing at all.

>
>The program deals with the issue of the reference equivalent to a null
>pointer -- ie what one would get if one could dereference a null pointer
>and asssign the result to a reference.  As the following shows, at least
>under cfront 2.0, the dereference followed by assignment to a reference is
>a conceptual dereference only  --not actually done in machine code--  and
>thus causes no null-pointer fault.  While the following example is weak,
>I claim assigning a "null" to a reference is useful when defining
>iterators returning references rather than pointers.

You have shown that it is useful, and you have done so quite clearly.  Did
anyone suggest that is wasn't useful?

>
>[I don't believe this issue is covered in the C++ references]

Agreed, but then it doesn't have to be.  In effect, on the fifth iteration
you make `row' be a reference to a hypothetical `ow' object which happens
to be located at address zero.  You then check to see if the address of the
refered to object is zero before you do anything rash to it.

It's so simple that it is intutive.  Why clutter up the manual?

P.S.  My rule of thumb is that references are exactly like const pointers
only different (due to the implicit dereferencing).

>
>********/
>
>class OW
>{
>    char ow[4];
>public:
>    OW(char* p) { strncpy(ow, p, 4); }
>    void print() { printf("%s ", ow); }
>};
>
>OW how("how"); OW now("now"); OW bow("bow"); OW wow("wow");
>OW* pow[] = { &how, &now, &bow, &wow, 0 };
>
>int main()
>{
>    int i = 0;
>    while (1)
>        {
>        OW& row = *pow[i++];
>        if (!&row) break;
>        row.print();
>        }
>    return 0;
>};

jimad@microsoft.UUCP (Jim ADCOCK) (03/08/90)

In article <25EB8EE8.8462@paris.ics.uci.edu> rfg@ics.uci.edu (Ronald Guilmette) writes:
>In article <51083@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>>Is the following program legal, illegal, or undefined?
>>[It compiles and runs "correctly" under cfront 2.0]
>Not surprizing at all.
Always surprising 1/2 :-)
>>The program deals with the issue of the reference equivalent to a null
>>pointer -- ie what one would get if one could dereference a null pointer
>>and asssign the result to a reference.  As the following shows, at least
>>under cfront 2.0, the dereference followed by assignment to a reference is
>>a conceptual dereference only  --not actually done in machine code--  and
>>thus causes no null-pointer fault.  While the following example is weak,
>>I claim assigning a "null" to a reference is useful when defining
>>iterators returning references rather than pointers.
>
>You have shown that it is useful, and you have done so quite clearly.  Did
>anyone suggest that is wasn't useful?

No, but I suggest that it has not yet been defined as "legal", and in
fact there are hints in the reference that this behaviour should presently
be considered "undefined."  If this behaviour is defined as "legal,"
then this is going to have impact on mapping C++ to systems with 
[hardware support for] typed pointers, such as "Rekursiv."

>>[I don't believe this issue is covered in the C++ references]
>
>Agreed, but then it doesn't have to be.  In effect, on the fifth iteration
>you make `row' be a reference to a hypothetical `ow' object which happens
>to be located at address zero.  You then check to see if the address of the
>refered to object is zero before you do anything rash to it.
>
>It's so simple that it is intutive.  Why clutter up the manual?

Simple, but wrong.  Reading the reference, I find:

"A constant expression that evaluates to 0 is converted to a pointer,
commonly called the null pointer.  It is guaranteed that this value will
produce a pointer distinguishable from a pointer to any object."

If the null pointer does not point to any object, then why should
I assume that I can take the address of that "object" which isn't an
object?  What guarantee is there that an object that doesn't exist 
even has an address?  And why should that address be "0" ???

It may be that many present systems represent *some* C++ null pointers
as 32 bits all zero, but this is not the same as saying making such
an assumption is portable to future systems.  In particular, a system
with [hardware support for] typed pointers would not have null pointers 
represented as 32 bits all zero.

Also, note that the "0" that creates a null pointer must be a constant
expression -- allowing a compiler to construct a special representation
for null pointers at compile time.  Also note:  This means the fairly
common "C" coding technique of assigning to a pointer a runtime expression 
that evaluates to zero is not guaranteed to be legal.  And assigning
one null pointer to a different type of pointer need not keep the same
bit representations.

>P.S.  My rule of thumb is that references are exactly like const pointers
>only different (due to the implicit dereferencing).

Like many rules of thumb, this one is quite wrong.  References are
not even first class citizens.  Like register variables, references don't
even have an address.  sizeof(aRefToOb) is the sizeof(Ob), whereas 
sizeof(aPtrToOb) is typically the native size of a pointer on your system
[2 or 4, typical].  Further, using references may generate temporaries
where using pointers does not.

So, I interpret your response to be: "This is presently undefined,
but who cares, its trivial."  Well, I care, and I claim its not
a trivial issue, but rather has important impact for the mapping of
C++ onto systems with typed pointers.  If making null references
*is* legal, let someone in the know state so uneqivocally, and I
can get on with my programming.  I'd like to see this made explicetly
legal -- but doing so may have a sharp negative impact on systems
with typed pointers.

rfg@ics.uci.edu (Ronald Guilmette) (03/10/90)

In article <52081@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>...  Reading the [ C++ ] reference, I find:
>
>"A constant expression that evaluates to 0 is converted to a pointer,
>commonly called the null pointer.  It is guaranteed that this value will
>produce a pointer distinguishable from a pointer to any object."
>
>If the null pointer does not point to any object, then why should
>I assume that I can take the address of that "object" which isn't an
>object?  What guarantee is there that an object that doesn't exist
>even has an address?  And why should that address be "0" ???
>
>It may be that many present systems represent *some* C++ null pointers
>as 32 bits all zero, but this is not the same as saying making such
>an assumption is portable to future systems.  In particular, a system
>with [hardware support for] typed pointers would not have null pointers
>represented as 32 bits all zero.
>
>Also, note that the "0" that creates a null pointer must be a constant
>expression -- allowing a compiler to construct a special representation
>for null pointers at compile time.  Also note:  This means the fairly
>common "C" coding technique of assigning to a pointer a runtime expression
>that evaluates to zero is not guaranteed to be legal.  And assigning
>one null pointer to a different type of pointer need not keep the same
>bit representations.

Jim,

I see that I should not have been nearly so cavalier in response to your
questions about "null references".  You raise some important questions.

Note however that many of these questions apply equally well to the C standard.
For instance, in C, should the following be considered legal?

	void *vp;

	vp = 0;
	vp = &(*vp);

Also, you noted the obscure restriction that a null pointer must be
something created from "an integral constant expression with the value 0"
(see ANSI C standard 3.2.2.3).  This could be an issue for both C and
C++, since it would seem to place in doubt the validity of the following:

	int nil ()
	{
		return 0;
	}

	...

		void *vp;

		vp = (void *) nil ();

I am cross posting this to comp.std.c just to see if anyone there will
clarify for us the legality (or illegality) of the two examples above
(and possibly also the relevant rationale).

>So, I interpret your response to be: "This is presently undefined,
>but who cares, its trivial."  Well, I care, and I claim its not
>a trivial issue, but rather has important impact for the mapping of
>C++ onto systems with typed pointers.  If making null references
>*is* legal, let someone in the know state so uneqivocally, and I
>can get on with my programming.  I'd like to see this made explicetly
>legal -- but doing so may have a sharp negative impact on systems
>with typed pointers.

At the very least, these issues should be clarified a bit, both for C++
and for C.


// Ron Guilmette (rfg@ics.uci.edu)
// C++ Entomologist
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

henry@utzoo.uucp (Henry Spencer) (03/11/90)

In article <25F8D2FB.10981@paris.ics.uci.edu> rfg@ics.uci.edu (Ronald Guilmette) writes:
>...noted the obscure restriction that a null pointer must be
>something created from "an integral constant expression with the value 0"
>(see ANSI C standard 3.2.2.3).  This could be an issue for both C and
>C++, since it would seem to place in doubt the validity of the following:
>
>	int nil ()
>	{
>		return 0;
>	}
>
>	...
>
>		void *vp;
>
>		vp = (void *) nil ();

This code is illegal in C, and always has been.  There is no general
relationship between the integer value 0 and the null pointer.  An
integer *constant* value equal to zero in a pointer context is automatically
converted to a null pointer; this may be a non-trivial conversion, and is
guaranteed valid only at compile time.  The result of converting the
integer value 0 to a pointer at run time is implementation-defined.
-- 
MSDOS, abbrev:  Maybe SomeDay |     Henry Spencer at U of Toronto Zoology
an Operating System.          | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

henry@utzoo.uucp (Henry Spencer) (03/11/90)

In article <1990Mar11.015305.28264@utzoo.uucp> I wrote:
>This code is illegal in C, and always has been... The result of converting the
>integer value 0 to a pointer at run time is implementation-defined.

Oops; change "illegal" to "naive, improper, unreliable, and unportable".
It's thoroughly bad code, but it is not actually illegal, as my own
further comments implied.
-- 
MSDOS, abbrev:  Maybe SomeDay |     Henry Spencer at U of Toronto Zoology
an Operating System.          | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

haug@almira.uucp (Brian R Haug) (03/12/90)

In article <1990Mar11.015305.28264@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>In article <25F8D2FB.10981@paris.ics.uci.edu> rfg@ics.uci.edu (Ronald Guilmette) writes:
>>...noted the obscure restriction that a null pointer must be
>>something created from "an integral constant expression with the value 0"
>>(see ANSI C standard 3.2.2.3).  This could be an issue for both C and
>>C++, since it would seem to place in doubt the validity of the following:
>>
>>	int nil ()
>>	{
>>		return 0;
>>	}
>>
>>	...
>>
>>		void *vp;
>>
>>		vp = (void *) nil ();
>
>This code is illegal in C, and always has been.  There is no general
>relationship between the integer value 0 and the null pointer.  An
>integer *constant* value equal to zero in a pointer context is automatically
>converted to a null pointer; this may be a non-trivial conversion, and is
>guaranteed valid only at compile time.  The result of converting the
>integer value 0 to a pointer at run time is implementation-defined.

Henry,
    I beg to differ.  I see where you are coming from with the wording:
"an integral constant expression with the value 0", but in K&R First edition,
page 97, there is an explicit example which 0 is returned by a function
returning a pointer to a character.  Also, from the text "C guarantees that no
pointer that validly points at data will contain zero, so a return value of
zero can be used to signal an abnormal event,..."
    Also, from Appendix A, section 7.7 (Equality operators) "A pointer may be
compared to an integer, but the result is machine dependent unless the integer
is the constant 0.  A pointer to which 0 has been assigned is guaranteed no to
point to any object, and will appear to be equal to 0; in conventional usage,
such a pointer is considered to be null."  This implies to me that I can
compare a pointer to any arbitrary expression which evaluates to 0 and have
machine independent behavior.  And from section 7.14, "it is guaranteed that
the assignment of the constant 0 to a pointer will produce a null pointer
distinguishable from a pointer to any object."  Since the "BNF" for this
section lists "lvalue = expression," I would assume that if expression
evaluates to 0 then the pointer will produce a null pointer distinguishable
from a pointer to any object.
    As far as ANSI C goes, I do not have easy access to the standard at the
current time, nor the experience reading it.  However, I would have expected
this behavior to be brought forward (hopefully not a silly idea, but we'll
see).
     This was a quick scan through K&R, so I hope I have not taken anything
out of context.  The only reason this objection stuck in my mind is that I had
once thought of doing an implementation where NULL != 0, but after further
reading convinced myself the implementation would be invalid.

			Share and Enjoy!

			      Brian Haug

Disclaimer:  These opinions are mine alone.

tim@nucleus.amd.com (Tim Olson) (03/12/90)

In article <1990Mar11.222634.2701@almira.uucp> haug@Columbia.NCR.COM (Brian Haug) writes:
| In article <1990Mar11.015305.28264@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
| >This code is illegal in C, and always has been.  There is no general
| >relationship between the integer value 0 and the null pointer.  An
| >integer *constant* value equal to zero in a pointer context is automatically
| >converted to a null pointer; this may be a non-trivial conversion, and is
| >guaranteed valid only at compile time.  The result of converting the
| >integer value 0 to a pointer at run time is implementation-defined.
| 
| Henry,
|     I beg to differ.  I see where you are coming from with the wording:
| "an integral constant expression with the value 0", but in K&R First edition,
| page 97, there is an explicit example which 0 is returned by a function
| returning a pointer to a character.

That is a correct use.  Just as in an assignment to a pointer type,
the compiler knows that it is to coerce the integral constant 0 to the
machine-specific nil representation for that pointer type.

This differs from the case that was being discussed, in that the
return value is specifically declared to be a pointer type.  In the
previous discussion, the return value was declared "int".


|     Also, from Appendix A, section 7.7 (Equality operators) "A pointer may be
| compared to an integer, but the result is machine dependent unless the integer
| is the constant 0.  A pointer to which 0 has been assigned is guaranteed no to
	 ^^^^^^^^^^
| point to any object, and will appear to be equal to 0; in conventional usage,
| such a pointer is considered to be null."  This implies to me that I can
| compare a pointer to any arbitrary expression which evaluates to 0 and have
| machine independent behavior.

Not unless it is a constant expression that evaluates to 0.  Constant
expressions are a subset of expressions that involve only integral
constants and certain operators -- function calls are not allowed.


	-- Tim Olson
	Advanced Micro Devices
	(tim@amd.com)

henry@utzoo.uucp (Henry Spencer) (03/13/90)

In article <1990Mar11.222634.2701@almira.uucp> haug@Columbia.NCR.COM (Brian Haug) writes:
>    I beg to differ.  I see where you are coming from with the wording:
>"an integral constant expression with the value 0", but in K&R First edition,
>page 97, there is an explicit example which 0 is returned by a function
>returning a pointer to a character...

K&R1 was kind of sloppy about this, partly because the pdp11 let you get
away with a lot.  However, the definition of C was the reference manual,
not the textbook...

>    Also, from Appendix A, section 7.7 (Equality operators) "A pointer may be
>compared to an integer, but the result is machine dependent unless the integer
>is the constant 0.  A pointer to which 0 has been assigned is guaranteed no to
>point to any object, and will appear to be equal to 0; in conventional usage,
>such a pointer is considered to be null."  This implies to me that I can
>compare a pointer to any arbitrary expression which evaluates to 0 and have
>machine independent behavior...

How do you conclude this from a statement which explicitly says that the
comparison is machine-dependent unless the *constant* 0 is used?

>And from section 7.14, "it is guaranteed that
>the assignment of the constant 0 to a pointer will produce a null pointer
>distinguishable from a pointer to any object."  Since the "BNF" for this
>section lists "lvalue = expression," I would assume that if expression
>evaluates to 0 then the pointer will produce a null pointer distinguishable
>from a pointer to any object.

Again, you're ignoring the fact that the text quite explicitly calls for
the *constant* 0 to be used.  The BNF defines only the syntax, not the
semantics and type structure; it must be read together with the text to
understand the language.

>    As far as ANSI C goes, I do not have easy access to the standard at the
>current time, nor the experience reading it.  However, I would have expected
>this behavior to be brought forward...

ANSI C carries forward the rules from K&R1 and later references:  a null
pointer is an integer *constant* zero converted to a pointer type.  ANSI
does permit a *constant* expression which evaluates to zero, e.g. "1-1",
but that does not remove the requirement that the value be known at
compile time.

>... I had
>once thought of doing an implementation where NULL != 0, but after further
>reading convinced myself the implementation would be invalid.

Not invalid, no; I think there are one or two such.  Unwise, yes, because
many many programs are sloppy about this and have to be fixed to run on
such an implementation.
-- 
MSDOS, abbrev:  Maybe SomeDay |     Henry Spencer at U of Toronto Zoology
an Operating System.          | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

gordon@sneaky.UUCP (Gordon Burditt) (03/13/90)

Implementations in which the value of a null pointer is not zero are
legal, but painful.  

>    I beg to differ.  I see where you are coming from with the wording:
>"an integral constant expression with the value 0", but in K&R First edition,
>page 97, there is an explicit example which 0 is returned by a function

The example shows the integer constant 0 being returned from a function that
returns a pointer to character.  Fine.  The compiler can convert it
to:
	movl	#0xdeadbee1,d0
	rts
at compile time.  The compiler knows it has to convert 0 in a return statement
to the type of the return type of the function.

>returning a pointer to a character.  Also, from the text "C guarantees that no
>pointer that validly points at data will contain zero, so a return value of
>zero can be used to signal an abnormal event,..."

"contain 0": that combination of bits that results from executing:
	p = 0;
commonly implemented by:
	movl	#0xdeadbee1,_p

>    Also, from Appendix A, section 7.7 (Equality operators) "A pointer may be
>compared to an integer, but the result is machine dependent unless the integer
>is the constant 0.  A pointer to which 0 has been assigned is guaranteed no to
        ^^^^^^^^^^
>point to any object, and will appear to be equal to 0; in conventional usage,

"appear to be equal to zero" means p == 0 is true, not something involving
peeking at bits in a machine register.

>such a pointer is considered to be null."  This implies to me that I can
>compare a pointer to any arbitrary expression which evaluates to 0 and have

It sure doesn't imply that to me.  The zero being discussed is constant 0, 
not an int variable containing 0.  The comparison p == 0 (to yield a "boolean" 
value, not a branch) might be translated as:

	cmpl	#0xdeadbee1,_p
	jeq	.L5001
	movl	#0,d0
	jmp	.L5002
.L5001: movl	#1,d0
.L5002:

Slow, yes.  But a perfectly legal implementation.

>machine independent behavior.  And from section 7.14, "it is guaranteed that
>the assignment of the constant 0 to a pointer will produce a null pointer
                       ^^^^^^^^^^
>distinguishable from a pointer to any object."  Since the "BNF" for this
>section lists "lvalue = expression," I would assume that if expression
>evaluates to 0 then the pointer will produce a null pointer distinguishable
>from a pointer to any object.

Where do you get that assumption?  It is legal to assign an integer expression
to a pointer, but that doesn't make it machine-independent.  The BNF
certainly doesn't say anything explicitly about the machine-dependent
characteristics of addition with overflow, division by zero, or shifts by
negative amounts, so why should it be expected to say something in this
case?  

Just before the section you quoted, it says, "The compilers currently allow 
a pointer to be assigned to an integer, an integer to a pointer, and a 
pointer to a pointer of another type.  The assignment is a pure copy 
operation, with no conversion.  This usage is unportable, and may produce 
pointers which cause addressing exceptions when used."

Since "an integer variable containing zero" does not fit the exception
for the integer constant zero, assigning one to a pointer IS unportable.

>    As far as ANSI C goes, I do not have easy access to the standard at the
>current time, nor the experience reading it.  However, I would have expected
>this behavior to be brought forward (hopefully not a silly idea, but we'll
>see).

ANSI C seems quite careful NOT to require that the representation of a
null pointer is all zero bits.

>     This was a quick scan through K&R, so I hope I have not taken anything
>out of context.  The only reason this objection stuck in my mind is that I had
>once thought of doing an implementation where NULL != 0, but after further
>reading convinced myself the implementation would be invalid.

Don't confuse NULL and the bits used to represent a null pointer.
NULL == 0 must be true.  assembly_language_representation_of(NULL) == 0
need not be true.  An implementation in which ((char *) NULL) == 0 and 
((char *) NULL) == 0xdeadbee1 are both true is possible, provided that 
assembly-language address 0xdeadbee1 isn't a possible address for a variable.

						Gordon L. Burditt
						sneaky.lonestar.org!gordon

henry@utzoo.uucp (Henry Spencer) (03/14/90)

In article <1990Mar12.175613.12082@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>>... in K&R First edition,
>>page 97, there is an explicit example which 0 is returned by a function
>>returning a pointer to a character...
>
>K&R1 was kind of sloppy about this, partly because the pdp11 let you get
>away with a lot...

Oops, I goofed.  (Yesterday was a bad day...)  There is no problem with
this.  The function is known to return pointer to character, so the
"return(0)" [actually "return(NULL)", prefaced by "#define NULL 0"]
gets compiled as if it were "return((char *)0)" and everything is fine.
By the time the value reaches the caller, it has already been converted
to a pointer.  Automatic conversion in "return" has been in the language
since well before K&R1 (I can remember when it arrived, but that was a
*long* time ago).

On closer inspection of K&R1, at the prodding of a friend, no, K&R1 is
*not* sloppy about this.  I can't find any instance where the rules are
being violated.  Conversion of zero to pointer is always conversion of the
constant 0.
-- 
MSDOS, abbrev:  Maybe SomeDay |     Henry Spencer at U of Toronto Zoology
an Operating System.          | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

guy@auspex.auspex.com (Guy Harris) (03/14/90)

>    I beg to differ.  I see where you are coming from with the wording:
>"an integral constant expression with the value 0", but in K&R First edition,
>page 97, there is an explicit example which 0 is returned by a function
>returning a pointer to a character.

Since the compiler knows that the function is supposed to return a "char
*", the compiler knows that the integral constant with the value 0 being
returned by that function should be converted to a null pointer-to-char.
In other words, said function doesn't return an integral value of 0, it
returns a null pointer-to-char.  This conversion is no different from
the conversion in

	char *p;

	p = 0;

or

	extern void foo(char *p);

	foo(0);

or

	char *p;

	if (p == 0)
		...

or even

	char *p;

	if (!p)		/* equivalent to previous example */
		...

The same applies to:

>Also, from the text "C guarantees that no
>pointer that validly points at data will contain zero, so a return value of
>zero can be used to signal an abnormal event,..."

since if your function returns a pointer value, it had better be defined
as doing so....

ken@argus.UUCP (Kenneth Ng) (03/14/90)

In article <1990Mar12.175613.12082@utzoo.uucp>, henry@utzoo.uucp (Henry Spencer) writes:
: ANSI C carries forward the rules from K&R1 and later references:  a null
: pointer is an integer *constant* zero converted to a pointer type.  ANSI
: does permit a *constant* expression which evaluates to zero, e.g. "1-1",
: but that does not remove the requirement that the value be known at
: compile time.
[edit]
: >once thought of doing an implementation where NULL != 0, but after further
: >reading convinced myself the implementation would be invalid.
: Not invalid, no; I think there are one or two such.  Unwise, yes, because
: many many programs are sloppy about this and have to be fixed to run on
: such an implementation.

I'm confused, is a non zero NULL pointer valid or not?  I'm not asking if
it will break 90% of the programs out there that use 0 instead of NULL.
On a 370 here I'd love to define NULL as -1 because it will cause an
immediate addressing exception if it is referenced.  But, I was told that
NULL is defined as always being the value zero.

: MSDOS, abbrev:  Maybe SomeDay |     Henry Spencer at U of Toronto Zoology
: an Operating System.          | uunet!attcan!utzoo!henry henry@zoo.toronto.edu


-- 
Kenneth Ng: Post office: NJIT - CCCC, Newark New Jersey  07102
uucp !andromeda!galaxy!argus!ken *** NOT ken@bellcore.uucp ***
bitnet(prefered) ken@orion.bitnet  or ken@orion.njit.edu

henry@utzoo.uucp (Henry Spencer) (03/15/90)

In article <1623@argus.UUCP> ken@argus.UUCP (Kenneth Ng) writes:
>I'm confused, is a non zero NULL pointer valid or not?  I'm not asking if
>it will break 90% of the programs out there that use 0 instead of NULL.
>On a 370 here I'd love to define NULL as -1 because it will cause an
>immediate addressing exception if it is referenced.  But, I was told that
>NULL is defined as always being the value zero.

NULL is just a convenient way of writing the constant 0, for all practical
purposes.  It is *not*, by itself, a null pointer, because there is no
"generic null pointer" type.  NULL has to be converted to a specific pointer
type to become a null pointer.  That (compile-time) conversion may well
change the representation in some strange way.  `NULL' must remain 0 (or a
close equivalent), but `(foo *)NULL' can be a different story.

There is absolutely nothing wrong with having a pointer representation in
which the bit pattern for a null pointer is not all zeros... except that
there are a lot of old, badly-written programs which will break.  Thus my
earlier comment that it is valid but unwise.
-- 
MSDOS, abbrev:  Maybe SomeDay |     Henry Spencer at U of Toronto Zoology
an Operating System.          | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

guy@auspex.auspex.com (Guy Harris) (03/15/90)

>I'm confused, is a non zero NULL pointer valid or not?

The confusion stems from confusion over the meaning of "non-zero".

4.1.5:

	...

	The macros are

	     NULL

	which expands to an implementation-defined null pointer
	constant; ...

3.2.2.3:

	...

	   An integral constant expression with the value 0, or such an
	expression cast to type "void *", is called a *null pointer
	constant*.

So if some implementation defines NULL as something other than 0 or
(void *)0 or (17 - 17) or ((void *)(17 - 17)) or..., the implementation
is not a valid C implementation. 

*However*, this does *not* mean that the implementation must *represent*
a null pointer of any type as a bit string of all zero bits.

>I'm not asking if it will break 90% of the programs out there that
>use 0 instead of NULL.

Defining NULL as something other than the values listed (or implied...)
and having that be the only null pointer constant will break all of the
programs out there at use 0 instead of NULL.  Thus, doing so would be
invalid.

Representing a null pointer constant as something other than a bit
string of all zero bits will not break *any* valid ANSI C program.  It
will break some *in*valid programs, which is why Henry described it as
"unwise" even though it's completely valid.

>On a 370 here I'd love to define NULL as -1 because it will cause an
>immediate addressing exception if it is referenced.  But, I was told that
>NULL is defined as always being the value zero.

If by "define NULL as -1" you mean putting in something like

	#define	NULL	-1

that would, indeed, be invalid.

If you mean changing your 3*0 C implementation such that null pointers
are represented by 32 1 bits, *including making sure that the statements

	char *p;

	p = 0;

assigns a value of 32 1 bits to "p"* (this is a bit that confuses a lot
of people who think that the "p = 0;" is obliged to assign an
all-zero-bits value to "p"), then it would be valid as long as
you make sure this representation convention is followed consistently;
you also have to make sure that non-C code that calls C code or is
called by C code obeys this convention.

karl@haddock.ima.isc.com (Karl Heuer) (03/15/90)

In article <1990Mar14.164539.23685@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>There is absolutely nothing wrong with having a pointer representation in
>which the bit pattern for a null pointer is not all zeros... except that
>there are a lot of old, badly-written programs which will break.  Thus my
>earlier comment that it is valid but unwise.

Note that "p = 0", "p == 0", "!p", "char *f() { return 0; }" are *not*
examples of such badly-written code; they may be bad style, but the compiler
is required to generate correct code involving a true null pointer.  The only
"dangerous" context (other than hacking with unions and such) is when a null
pointer constant is being passed as an argument to a function.  (In C++ and
ANSI C, any argument not covered by a prototype.  In old C, any function
argument at all.)  In particular, neither of the two calls
	execl("/bin/sh", "sh", "-i", 0);
	execl("/bin/sh", "sh", "-i", NULL);
is correct; it should be written as either of
	execl("/bin/sh", "sh", "-i", (char *)0);
	execl("/bin/sh", "sh", "-i", (char *)NULL);

But this problem can occur even without strange null pointers: such sloppy
code will already break on certain implementations where pointers and ints
have different lengths.

Karl W. Z. Heuer (karl@ima.ima.isc.com or harvard!ima!karl), The Walking Lint
Followups to comp.lang.c.

shopiro@alice.UUCP (Jonathan Shopiro) (03/16/90)

To me the interesting question in this null pointer business is
whether there is any circumstance where it is legal to say *p
where p is a null pointer.

For example, it is definitely legal to write

	void   f(int* p)
	{
		if (p == 0) ...
	}

	int*   x = 0;
	f(x);

It is not obvious (to me) if it is legal in C to write

	void   g(int* p)
	{
		if (&(*p) == 0) ...
	}

	int*   x = 0;
	g(x);

But nobody would write that code anyway, so who cares.

However, in C++ there is an analog that is very likely to be written.

	void   h(int& r)
	{
		if (&r == 0) ...
	}

	int*   x = 0;
	h(*x);

It seems to me that g() and h() are two ways of writing the same thing.
I think it would be nice if they were both legal.

I think the fundamental issue here is when is a pointer dereferenced?
(Since it is clearly illegal to dereference the null pointer).  I don't
se why writing

	*p

alone, or

	&*p

should dereference p.
-- 
		Jonathan Shopiro
		AT&T Bell Laboratories, Warren, NJ  07060-0908
		research!shopiro   (201) 580-4229

roberto@ssd.csd.harris.com (Roberto Shironoshita) (03/17/90)

In article <1623@argus.UUCP> ken@argus.UUCP (Kenneth Ng) writes:
> I'm confused, is a non zero NULL pointer valid or not?  I'm not asking if
> it will break 90% of the programs out there that use 0 instead of NULL.
> On a 370 here I'd love to define NULL as -1 because it will cause an
> immediate addressing exception if it is referenced.  But, I was told that
> NULL is defined as always being the value zero.

The Dec. 88 draft states that <stddef.h> defines the macro NULL, which
expands to an implementation-defined null pointer constant (section 4.1.5).

\begin{quote}
	An integral constant expression with the value 0, or such an
	expression cast to type void *, is called a _null pointer
	constant_. (...)
\end{quote}


So the implementation has two choices:

	#define NULL	0
or
	#define NULL	(void *)0

(where '0' stands for an integral constant expression that evaluates
to 0).

Your compiler is free to use whatever address it pleases when
translating code, so long as the program does not consider it a
valid address.  Note that dereferencing a NULL pointer causes
undefined behavior.
--
                               ||   Internet: shirono@ssd.csd.harris.com
     Roberto Shironoshita      ||
      Harris Corporation       ||             ...!novavax-\
   Computer Systems Division   ||   UUCP:                  -!hcx1!shirono
                               ||             ...!uunet---/
DISCLAIMER: The opinions expressed here are my own; they in no way reflect the
            opinion or policies of Harris Corporation.

throopw@sheol.UUCP (Wayne Throop) (03/18/90)

> From: guy@auspex.auspex.com (Guy Harris)
>> I'm confused, is a non zero NULL pointer valid or not?
> The confusion stems from confusion over the meaning of "non-zero".

While what Guy says in explanation is certainly correct, I think
that the confusion is over the meaning of "NULL pointer", not
non-zero.  That is, Guy explained things thoroughly, but the basis
of the problem is the distinction between properties of a name
of a thing and properties of the thing itself.

In the phrase "NULL pointer" in C, there are TWO levels of naming
going on, either one of which could have the property "zeroness".

Or let me put it this way.

In C, the name of the nil pointer is called "NULL".

But that's only what the name is CALLED, you see.

The NAME of the nil pointer is "0".

The nil pointer itself can have any bit pattern it pleases. 



The above sequence of statements explains
   1) why NULL should only be used as a pointer (this is a convention).
      It shouldn't be used to mean ascii NUL
   2) why the only proper definition of the macro NULL is the string "0".
      (or, actually, any way to spell the constant zero in C)
      (though, granted, the advent of ANSI C means that the best
       definition of NULL is arguably the string "((void*)0)" and
       variants on this theme.  Nevertheless "0" remains proper.)
   3) in just what way C's nil pointer may have a non-zero bit pattern.

It omits explanations of why one should never (in K&R1 C) pass NULL
as an argument without a cast to a specific pointer type.

It also omits explanation of the fact that there isn't really one
nil pointer, or (necessarily) one nil pointer bit pattern.  (This
is due to the fact that all pointer values in C have specific
types.)

So, to conclude, "NULL", in C should never have a "non-zero" definition.

Any particular nil pointer value (eg, one named ((void*)0)) can have any
bit pattern the implementor of a C language system chooses. 

--
Wayne Throop <backbone>!mcnc!rti!sheol!throopw or sheol!throopw@rti.rti.org

henry@utzoo.uucp (Henry Spencer) (03/18/90)

In article <ROBERTO.90Mar16130444@ecx1.ssd.csd.harris.com> shirono@ssd.csd.harris.com writes:
>	An integral constant expression with the value 0, or such an
>	expression cast to type void *, is called a _null pointer
>	constant_. (...)

Note, also, that the cast to `void *' is legal not because it somehow
creates a generic null pointer -- there is no such thing in C -- but
because there may not be an integer type of the same size as a pointer,
and breakage of old programs is minimized if NULL is the same size as
a pointer.  (Even on machines where pointers are not all the same size,
one can at least reduce the breakage somewhat this way.)
-- 
MSDOS, abbrev:  Maybe SomeDay |     Henry Spencer at U of Toronto Zoology
an Operating System.          | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

sdm@cs.brown.edu (Scott Meyers) (03/18/90)

In article <10582@alice.UUCP> shopiro@alice.UUCP (Jonathan Shopiro) writes:
>	void   h(int& r)
>	{
>		if (&r == 0) ...
>	}
>
>	int*   x = 0;
>	h(*x);
>I think the fundamental issue here is when is a pointer dereferenced?
>(Since it is clearly illegal to dereference the null pointer).  I don't

Two comments:

    1.  In h(), r is a reference to an object.  By definition, no object
        can have an address that is equal to 0, so (&r == 0) should always
        fail.

    2.  In the call h(*x), we don't know precisely when *x is evaluated,
        but since r is initialized with that value, it must occur prior to
        executing any of the body of h().  If you let the evaluation
        semantics get any lazier, then you have to worry about this
        problem:

            int z = 1;                // global variable
            int fse() { z = 0; }      // function with side effects
            void foo(int y) {         // function that doesn't use its argument
              if (z) doSomething();
              else doSomethingElse();
            }

            int x;
            foo( fse() );

        When do you evaluate fse()?  If you don't initialize y with its
        value at the beginning of foo(), then when?  You've got to do it
        before z is evaluated...


Scott
sdm@cs.brown.edu

rfg@ics.uci.edu (Ronald Guilmette) (03/18/90)

In article <10582@alice.UUCP> shopiro@alice.UUCP (Jonathan Shopiro) writes:
>
>
>To me the interesting question in this null pointer business is
>whether there is any circumstance where it is legal to say *p
>where p is a null pointer.
...
>I think the fundamental issue here is when is a pointer dereferenced?
>(Since it is clearly illegal to dereference the null pointer).  I don't
>se why writing
...
>
>	&*p
>
>should dereference p.

I think this discussion is comming full circle.

If you scan backwards over the (20-30) postings on this issue you'll note that
I said almost exactly this same thing 20-30 messages ago.

It was quickly pointed out to me that I was I was being naive (which I now
freely admit that I was) because the question becomes confusing if p
contains a NULL pointer value.

Perhaps we should just take a vote and put the issue to bed.


// Ron Guilmette (rfg@ics.uci.edu)
// C++ Entomologist
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

shopiro@alice.UUCP (Jonathan Shopiro) (03/19/90)

In article <33188@brunix.UUCP>, sdm@cs.brown.edu (Scott Meyers) writes:
> In article <10582@alice.UUCP> shopiro@alice.UUCP (Jonathan Shopiro) writes:
> >	void   h(int& r)
> >	{
> >		if (&r == 0) ...
> >	}
> >
> >	int*   x = 0;
> >	h(*x);
> >I think the fundamental issue here is when is a pointer dereferenced?
> >(Since it is clearly illegal to dereference the null pointer).  I don't
> 
> Two comments:
> 
>     1.  In h(), r is a reference to an object.  By definition, no object
>         can have an address that is equal to 0, so (&r == 0) should always
>         fail.
But the question is, must a reference actually refer to an object?  Just as
a pointer can point to an object or to no-object, a reference could refer to
an object or to no-object.  Then, if a reference referred to no-object,
(&r == 0) would succeed.

Note that this has nothing whatsoever to do with the representation of a
null pointer.  If the reference r refers to no-object, then &r is the null
pointer (however it is represented).  It does have something to do with the
representation of a reference.  The representation would have to be capable
of storing a reference to no-object, as well as a reference to any object.
Since references are generally represented as pointers, this shouldn't be
a problem.

>     2.  In the call h(*x), we don't know precisely when *x is evaluated,
>         but since r is initialized with that value, it must occur prior to
>         executing any of the body of h().
The real question is not _when_ is *x evaluated, but __how_.  I claim that
*x is evaluated differently in

	int   i = *x;      // eval *x for rvalue

than in

	int*  p = &*x;     // eval *x for lvalue

and if x is the null pointer, the first is illegal and the second should
be legal, since x is never dereferenced.  The call h(*x) is analogous
to the second example.
-- 
		Jonathan Shopiro
		AT&T Bell Laboratories, Warren, NJ  07060-0908
		research!shopiro   (201) 580-4229

sdm@cs.brown.edu (Scott Meyers) (03/19/90)

In article <10595@alice.UUCP> shopiro@alice.UUCP (Jonathan Shopiro) writes:
>In article <33188@brunix.UUCP>, sdm@cs.brown.edu (Scott Meyers) writes:
>> In article <10582@alice.UUCP> shopiro@alice.UUCP (Jonathan Shopiro) writes:
>> >	void   h(int& r)
>> >	{
>> >		if (&r == 0) ...
>> >	}
>> >
>> >	int*   x = 0;
>> >	h(*x);
>> 
>> Two comments:
>> 
>>     1.  In h(), r is a reference to an object.  By definition, no object
>>         can have an address that is equal to 0, so (&r == 0) should always
>>         fail.
>But the question is, must a reference actually refer to an object?  Just as
>a pointer can point to an object or to no-object, a reference could refer to
>an object or to no-object.  Then, if a reference referred to no-object,
>(&r == 0) would succeed.

Yes, a reference must actually refer to an object.  (For the purposes of
this discussion, variables of build-in types, e.g., int, are objects.)
That's why references have to be initialized.  Pointers are a different
story entirely, because they don't have to be initialized.  The only way to
get a reference to no-object would be something like this:

    Object *ptr = 0;
    Object& ref = *ptr;

But here again *ptr has to be evaluated to initialize ref.

>>     2.  In the call h(*x), we don't know precisely when *x is evaluated,
>>         but since r is initialized with that value, it must occur prior to
>>         executing any of the body of h().
>The real question is not _when_ is *x evaluated, but __how_.  I claim that
>*x is evaluated differently in
>
>	int   i = *x;      // eval *x for rvalue
>
>than in
>
>	int*  p = &*x;     // eval *x for lvalue
>
>and if x is the null pointer, the first is illegal and the second should
>be legal, since x is never dereferenced.  The call h(*x) is analogous
>to the second example.

I disagree; I think the question of "when is *x evaluated" is as real a
question as there is.  Perhaps what you really want is a new "operator&*"
that has some kind of special semantics.  After all, in the presence of a
user-defined operator&, who says *x has to yield an lvalue in the
expression &*x?

Getting back to the original point of this discussion, I personally believe
that it would be very convenient to have "null references," but I also
believe that they don't exist and that making them exist would complicate a
language that already has more than its share of complications.  ("C++: The
Programming Language Instructor's Full Employment Act.")  Of course, I also
tend to believe that references are an idea whose time should never have
come.  Maybe someone can enlighten me: what do references offer besides
syntactic sugar?  From my limited vantage point, they're just like
pointers, but you can't new/delete them, you can't reassign them, and you
can't compare them against 0.  Don't get me wrong: I like sugar as much as
anybody else, but syntactic sweetness has never been C++'s strong suit.  In
the case of references, I find that the confusion outweighs the
convenience.

Scott
sdm@cs.brown.edu

sharam@munnari.oz.au (Sharam Hekmatpour) (03/19/90)

From article <33209@brunix.UUCP>, by sdm@cs.brown.edu (Scott Meyers):

> [...]
> Of course, I also
> tend to believe that references are an idea whose time should never have
> come.  Maybe someone can enlighten me: what do references offer besides
> syntactic sugar?

Nothing. But the syntactic sugar offered by references is so important in
that you can't eliminate them without losing much of the convenience
of C++. I can name two things:

[1] Efficient implementation of overloaded operators that involve large
    objects (eg, matrices) requires the operands to be references.
    Pointers simply won't work.

[2] A call to a function that returns a reference is an lvalue. This
    syntactic sugar is very useful.

> From my limited vantage point, they're just like
> pointers, but you can't new/delete them, you can't reassign them, and you
> can't compare them against 0.

There are things that you can do using references but not pointers.
See my previous posting for an example involving a 'delete' that zeros
pointers.

> Don't get me wrong: I like sugar as much as
> anybody else, but syntactic sweetness has never been C++'s strong suit.  In
> the case of references, I find that the confusion outweighs the
> convenience.

I disagree. High level languages are all about syntactic sugaring;
eliminate them and what you end up with is an assembler-like language.

> Scott
> sdm@cs.brown.edu

Sharam Hekmatpour
sharam@munnari.oz.au

rfg@ics.uci.edu (Ronald Guilmette) (03/19/90)

In article <3374@munnari.oz.au> sharam@munnari.oz.au writes:
>From article <33209@brunix.UUCP>, by sdm@cs.brown.edu (Scott Meyers):
>> Don't get me wrong: I like sugar as much as
>> anybody else, but syntactic sweetness has never been C++'s strong suit.  In
>> the case of references, I find that the confusion outweighs the
>> convenience.

In the case of references, I agree, but I fear that it is far to late to
get them removed from the language.

>I disagree. High level languages are all about syntactic sugaring;
>eliminate them and what you end up with is an assembler-like language.

Yes, but add too much "sugar" and you get Ada.


// Ron Guilmette (rfg@ics.uci.edu)
// C++ Entomologist
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

bart@videovax.tv.tek.com (Bart Massey) (03/19/90)

In article <10595@alice.UUCP> shopiro@alice.UUCP (Jonathan Shopiro) writes:
> [...]
> The real question is not _when_ is *x evaluated, but __how_.  I claim that
> *x is evaluated differently in
> 
> 	int   i = *x;      // eval *x for rvalue
> 
> than in
> 
> 	int*  p = &*x;     // eval *x for lvalue
> 
> and if x is the null pointer, the first is illegal and the second should
> be legal, since x is never dereferenced.  The call h(*x) is analogous
> to the second example.

The problem is that this precludes stupid compilers from generating code
which dereferences x as a side effect -- *x is an lvalue in C, but it is
still a value.  Some unsophisticated bottom-up tree-based code generator
may construct an expression tree for &*x which looks something like

	addr-of
	 |
	deref
	 |
 	 x
			 
and then generate code like

	move x to r0	; bottom of expression tree
	move *r0 to r1	; next level up
	move x to r2	; whoops! addr-of! search down the tree for the lvalue
	move r2 to p	; whew! that was close

and then not peephole optimize, since -O wasn't specified, leaving a
potentially dangerous dereference in.

Note that I can't give an example of such a C compiler offhand :-).  The
only analogue which comes immediately to mind is the old Apple MDS Pascal
compiler for the Lisa, which will do this with sizeof(x^^), but you're
probably not interested in *Pascal* :-).

Given the limited usefulness of the above construct, and given that I
otherwise might have the choice between a fairly good code generator and no
compiler at all, what I think I want is for writing *p to require p to point
at an object.  The ANSI C draft (3.3.3.2) seems to agree with me.

					Bart Massey
					..tektronix!videovax.tv.tek.com!bart
					..tektronix!reed.bitnet!bart

raeburn@athena.mit.edu (Ken Raeburn) (03/23/90)

In article <ROBERTO.90Mar16130444@ecx1.ssd.csd.harris.com>,
roberto@ssd.csd.harris.com (Roberto Shironoshita) writes:
[Dec '88 dpANS says:]
> \begin{quote}
> 	An integral constant expression with the value 0, or such an
> 	expression cast to type void *, is called a _null pointer
> 	constant_. (...)
> \end{quote}
> 
> So the implementation has two choices:
> 
> 	#define NULL	0
> or
> 	#define NULL	(void *)0

A small point of the logic of these arguments is nagging at me.  Does the
standard say that values equivalent to (void *)0 in pointer contexts
cannot be used for the definition of NULL?

Just to be weird, let us consider a machine/compiler combination which has
a integral type that has more bits than a pointer uses.  Say the machine
uses 24 bits for addressing, and a "long int" provides 32.  Could not
"(void *) 0xc0000000" be used for NULL?  The compiler would have to
understand that the top bits should be stripped out for comparisons, or
the architecture could provide a pointer-compare instruction; would it
need to provide anything else?  Or is this simply disallowed because the
standard is worded wrong?  Or is there something fundamental about it I am
missing that would make it fail miserably?

It would probably be a strange architecture that would do things this way,
but I expect there are some strange architectures out there, each with its
own reasons.

-- Ken

jc@atcmp.nl (Jan Christiaan van Winkel) (03/23/90)

From article <1990Mar23.072132.7307@athena.mit.edu>, by raeburn@athena.mit.edu (Ken Raeburn):
> a integral type that has more bits than a pointer uses.  Say the machine
> uses 24 bits for addressing, and a "long int" provides 32.  Could not
> "(void *) 0xc0000000" be used for NULL?  The compiler would have to
> 
> It would probably be a strange architecture that would do things this way,
> but I expect there are some strange architectures out there, each with its
> own reasons.

I believe the Apple Macintosh used to use the upper 8 bits of a pointer
to store information like relocatable or something. Now they've made
their system "32-bit clean" and pointers are the full 32 bits.
Note that I do not want to imply that the Mac has a strange architecture,
nor that it doesn't... :-)

JC.

guy@auspex.auspex.com (Guy Harris) (03/24/90)

>Does the standard say that values equivalent to (void *)0 in pointer
>contexts cannot be used for the definition of NULL?

Even if it did, what would the point be in doing so?  If the value truly
is equivalent, why is it any better than 0 or "(void *)0"?

karl@haddock.ima.isc.com (Karl Heuer) (03/24/90)

In article <1990Mar23.072132.7307@athena.mit.edu> Ken Raeburn <Raeburn@MIT.EDU> writes:
>Could not "(void *) 0xc0000000" be used for [the macro] NULL?

Oh, I suppose you *could* (by the as-if rule)--but the compiler still has to
generate the right answer for "0" and "(void *)0", so now you've got one more
"magic string" to worry about%.  If you're going to do that, you might as well
have it expand into "__builtin_NULL", which also allows compile-time detection
of improper NULL usage&.

Karl W. Z. Heuer (karl@ima.ima.isc.com or harvard!ima!karl), The Walking Lint
________
% Also, people who understand that "0" is *always* a valid definition for
  "NULL" will suspect that you don't know what you're doing.
& E.g. wrongly using NULL instead of '\0', or passing an uncasted NULL as a
  function argument not covered by a prototype.

roger@procase.UUCP (Roger H. Scott) (03/29/90)

I think people who take the address of reference variables should find another
language to program in (how about `C'?). ;->

cline@cheetah.ece.clarkson.edu (Marshall Cline) (04/05/90)

In article <PCG.90Apr2162557@odin.cs.aber.ac.uk> pcg@odin.cs.aber.ac.uk (Piercarlo Grandi) writes:
>...  I have an 'int *' pointer 'p' that I know
>points to a member 'b' of a class 'X'. I want the pointer to the whole
>class object. Will this be legal? Will this be safe? Will it have type
>'X *'?
>	auto X *x = p - &X::b; // (X *) = (int *) - (int X::*)

Subtracting two pointers doesn't give you a pointer.

>Suppose this works, and we have now a pointer 'x' to an object of class
>'X' as well. Will this be true?
>
>	p == (x + &X::b) // (int *) == ((X *) + (int X::*))

Adding two pointers doesn't give you a pointer.

I'm suspect you meant, in the first case, to cast both pointers to
char* (to add/subtract bytes not structs/ints), then cast (char*)&X::b
to an int, then subtract, then cast the difference back to X* (phew!).

Similarly you could contort the second example with (too many) casts. :-)

Marshall Cline
--
===============================================================================
Marshall Cline/ECE Department/Clarkson University/Potsdam NY 13676/315-268-3868
 cline@sun.soe.clarkson.edu, bitnet: BH0W@CLUTX, uunet!clutx.clarkson.edu!bh0w
  Career search in progress: ECE faculty. Research oriented. Will send vitae.
===============================================================================

cline@cheetah.ece.clarkson.edu (Marshall Cline) (04/05/90)

In article <CLINE.90Apr4142209@cheetah.ece.clarkson.edu> cline@cheetah.ece.clarkson.edu (Marshall Cline) writes:
(in other words, I just wrote):
> I'm suspect you meant, in the first case, to cast both pointers to
> char* (to add/subtract bytes not structs/ints), then cast (char*)&X::b
> to an int, then subtract, then cast the difference back to X* (phew!).

But Andrew Koneig recently pointed out that casting a pointer-to-member
to an int is undefined (it must be this way otherwise the first member of
a struct could come out 0, which is reserved to mean "ptr to nowhere").
My algorithm (cast, cast, subtract, cast) is therefore incorrect.
Apologies.

Marshall Cline
--
===============================================================================
Marshall Cline/ECE Department/Clarkson University/Potsdam NY 13676/315-268-3868
 cline@sun.soe.clarkson.edu, bitnet: BH0W@CLUTX, uunet!clutx.clarkson.edu!bh0w
  Career search in progress: ECE faculty. Research oriented. Will send vitae.
===============================================================================

diamond@tkou02.enet.dec.com (diamond@tkovoa) (04/09/90)

In article <58@cdss.UUCP> culliton@cdss.UUCP (Tom Culliton) writes:

>Another thought that stems from this and a little puzzle :-) how would you
>implement sizeof as a macro?

I don't think you can.  How can a macro compute sizeof(-3) without using the
builtin sizeof?  (And in case you manage that, here are two more for you:
sizeof -3 (no parentheses needed with an expression), and sizeof(int).)

-- 
Norman Diamond, Nihon DEC     diamond@tkou02.enet.dec.com
This_blank_intentionally_left_underlined________________________________________

pcg@aber-cs.UUCP (Piercarlo Grandi) (04/13/90)

In article <CLINE.90Apr4142209@cheetah.ece.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
  In article <PCG.90Apr2162557@odin.cs.aber.ac.uk> pcg@odin.cs.aber.ac.uk (Piercarlo Grandi) writes:
  >...  I have an 'int *' pointer 'p' that I know
  >points to a member 'b' of a class 'X'. I want the pointer to the whole
  >class object. Will this be legal? Will this be safe? Will it have type
  >'X *'?
  >	auto X *x = p - &X::b; // (X *) = (int *) - (int X::*)
  
  Subtracting two pointers doesn't give you a pointer.
  
  >Suppose this works, and we have now a pointer 'x' to an object of class
  >'X' as well. Will this be true?
  >
  >	p == (x + &X::b) // (int *) == ((X *) + (int X::*))
  
  Adding two pointers doesn't give you a pointer.

My point here, and I post this in case somebody else missed it, was precisely
that this does not work, even if member pointers have been described as if
they were relative pointers, to the point (;->) that they are implemented as
the offset of the member, plus one to avoid confusion between the zero'th
offset and the binary pattern used to represent in many implementations the
null (absolute) pointer.

My article was a condemnation of existing member pointers precisely because
they are only half baked relative pointers, and the above examples don't work,
which they should if relative pointers were allowed.

I also suggested some way to define the semantics of proper relative
pointers as a C++ primitive, even if, given the ability to overload pointer
dereferencing operator, and given either casts or templates, one can already
define relative pointers (used by some as 'smart pointers' to define self
relocating, sharable data areas).
-- 
Piercarlo "Peter" Grandi           | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk