What is this?

E

Eric

What is this?

( char * ) &( ( struct aStruct * ) 0 )

It looks like it's taking the address of something that contains a
pointer to null, and casting it to a pointer to char.

(Actually, technically, the address of a pointer to the beginning of a
struct aStruct, that starts at address 0).

Or, is it taking the pointer to null itself, and casting that to a
pointer to char?
 
J

James Kuyper

Eric said:
What is this?

( char * ) &( ( struct aStruct * ) 0 )

It looks like it's taking the address of something that contains a
pointer to null, and casting it to a pointer to char.

No, it looks like something that attempts to take the address of a null
pointer value. As such, it's a constraint violation (6.5.3.2p1).
 
R

Richard Tobin

Eric said:
What is this?

( char * ) &( ( struct aStruct * ) 0 )

As it is, it's an error. If it were

( char * ) &( ( struct aStruct * ) 0 )->x

(assuming x is a member of the struct), then it would be an
almost-plausible (though theoretically non-portable) imitation of
offsetof().

-- Richard
 
B

Ben Bacarisse

Eric said:
What is this?

( char * ) &( ( struct aStruct * ) 0 )

It is a constraint violation. That means that a conforming compiler
must issue a diagnostic. The result of a cast is not an lvalue and
the operand of & must be an lvalue.
It looks like it's taking the address of something that contains a
pointer to null, and casting it to a pointer to char.

(Actually, technically, the address of a pointer to the beginning of a
struct aStruct, that starts at address 0).

Not really. Once you have a constraint violation the program is
essentially meaningless, but if we put that to one side (i.e. we
pretend that the stated constraint is missing from the C
specification) then all we have is an attempt to take the address of a
null pointer constant. Null pointers (constant or otherwise) do not
point at anything -- specifically they don't point to an object at
"address zero".

The context of the code might explain the mystery, but it is hard to
guess any intent (at least I can't). Maybe there is a typo?
 
B

Ben Bacarisse

The contents of the pointer are irrelevant since we're taking its ADDRESS.
Taking the address of a null pointer is perfectly fine (and casting this
to char* is too):

Yes, true, but I don't think I contradicted that, did I? I was keen
to correct the idea that (T *)0 is pointer to a T "at address zero".

<snip>
 
J

jameskuyper

Ben said:
It is a constraint violation. That means that a conforming compiler
must issue a diagnostic. The result of a cast is not an lvalue and
the operand of & must be an lvalue.


Not really. Once you have a constraint violation the program is
essentially meaningless, but if we put that to one side (i.e. we
pretend that the stated constraint is missing from the C
specification) then all we have is an attempt to take the address of a
null pointer constant. Null pointers (constant or otherwise) do not
point at anything -- specifically they don't point to an object at
"address zero".

The fact that this pointer is null is irrelevant. There would be no
problem if it were a null pointer object rather than a null pointer
constant. The problem is that it's a pointer value, not a pointer
object, and it is just as much of a problem whether or not the pointer
value is null.
The context of the code might explain the mystery, but it is hard to
guess any intent (at least I can't). Maybe there is a typo?

If it were followed by ->x, where "x" is one of the members of struct
aStruct, the only problem with this code would be the fact that it
attempts to de-reference a null pointer, rendering the behavior
undefined. For that reason, such a construct should never occur in
user code. However, on many implementations the actual behavior of
this construct is such that, when the result is converted to size_t,
it constitutes a conforming implementation of the offsetof() macro.
Since <stddef.h> is part of the implementation, it's perfectly
legitimate for the implementor to take advantage of that fact inside
of stddef.h, even though developers should not do so in user code.
 
B

Ben Bacarisse

jameskuyper said:
The fact that this pointer is null is irrelevant. There would be no
problem if it were a null pointer object rather than a null pointer
constant. The problem is that it's a pointer value, not a pointer
object, and it is just as much of a problem whether or not the pointer
value is null.

Total failure to communicate on my part, I guess, since two people
have now made this point! I only wanted to address the phrase "a
struct aStruct at address 0". The cast expression (struct aStruct *)0
is (as you know) just a null pointer of struct aStruct * type and has
nothing to do with an object of type struct aStruct (and I did point
out what the problem really is with the code right up front!).
 
B

Ben Bacarisse

Ben said:
blargg said:
Ben Bacarisse wrote: [...]
Null pointers (constant or otherwise) do not
point at anything -- specifically they don't point to an object at
"address zero".

The contents of the pointer are irrelevant since we're taking its ADDRESS.
Taking the address of a null pointer is perfectly fine (and casting this
to char* is too):

Yes, true, but I don't think I contradicted that, did I? I was keen
to correct the idea that (T *)0 is pointer to a T "at address zero".

Ahhh, I see now what you were getting at, that for example the
(non-portable) offsetof implementation based on it depends on a null
pointer's representation being all-zero,

#define offsetof(n,m) ((size_t) &((n*) 0)->m)

No, that was not what I meant. For one thing, that code does not
depend on the representation of a null pointer (it is non-portable
because it involves de-referencing a null pointer). Honestly, all I
wanted to say was "null pointers do not point at anything --
specifically they don't point to an object at address zero".

I won't comment on the rest for fear of complicating matters more.
 
E

Eric

The context of the code might explain the mystery, but it is hard to
guess any intent (at least I can't). Maybe there is a typo?

Good afternoon, Ben.

I apologize for not providing enough information... I was just trying
to minimize my post but I guess I minimized it too much. :)

A more detailed block of code is:

bool result;
bool GetByte( char *theAddr, int theCount, char *theDest);

char Dest[ SOME_SIZE ];

result =
GetByte( ( char * ) &( ( struct aStruct * ) 0 )->Addr,
1, ( char * ) ( &Dest ) );

My question is... does the struct aStruct live at address 0, or does
address 0 contain a pointer to wherever aStruct actually lives? Seems
to me like the "&" ahead of ( ( struct aStruct * ) 0 )->Addr would
indicate the latter...
 
E

Eric

that code does not
depend on the representation of a null pointer (it is non-portable
because it involves de-referencing a null pointer).

Technically, it's dereferencing a zero pointer, which isn't guaranteed
to be the same thing as a null pointer, right?

This code runs on a low-end microcontroller and I think the idea is
that they have something specific at address zero, that they want to
access.

Not really sure about that, though (yet).

See my other post today that shows a more complete block of code that
hopefully does a slightly better job of showing what they are trying
to do.
 
J

jameskuyper

Eric said:
Technically, it's dereferencing a zero pointer, which isn't guaranteed
to be the same thing as a null pointer, right?

(struct aStruct*)0 is a null pointer.
 
J

jameskuyper

Eric said:
Good afternoon, Ben.

I apologize for not providing enough information... I was just trying
to minimize my post but I guess I minimized it too much. :)

That's easy to do; it's still too minimal.
A more detailed block of code is:

bool result;
bool GetByte( char *theAddr, int theCount, char *theDest);

char Dest[ SOME_SIZE ];

result =
GetByte( ( char * ) &( ( struct aStruct * ) 0 )->Addr,
1, ( char * ) ( &Dest ) );

My question is... does the struct aStruct live at address 0, or does
address 0 contain a pointer to wherever aStruct actually lives? Seems
to me like the "&" ahead of ( ( struct aStruct * ) 0 )->Addr would
indicate the latter...

struct aStruct is not a particular struct that resides at a particular
location, it is a struct type. This code creates a null pointer of
type "struct aStruct*", and then dereferences it. The behavior is
undefined, which ends the discussion as far as the C standard is
concerned.

Pragmatically, on many implementations a null pointer points at an
actual memory location, one that strictly conforming C code cannot
access. The result of the above conversion is to treat that memory
location as if it was the start of an object of type "struct aStruct".
The rest of the expression creates a char* pointer that points at the
location at which the "Addr" member of such a struct would reside. We
don't know what GetByte() will do with that pointer - you haven't told
us what GetByte() does. I would guess that this call to GetBytes()
copies 1 byte from that location into Dest.

This is all highly non-portable.
 
R

Richard Tobin

Eric said:
Technically, it's dereferencing a zero pointer,
No...

which isn't guaranteed
to be the same thing as a null pointer, right?

Right.

But (type *)0 is always a null-pointer constant: if null pointers have
some odd representation, the compiler must recognise the construct and
generate that odd representation.

On such a system you may be able to get a "zero pointer" by casting
a non-constant zero or by setting a pointer using memcpy().

-- Richard
 
N

Nate Eldredge

Eric said:
The context of the code might explain the mystery, but it is hard to
guess any intent (at least I can't). Maybe there is a typo?

Good afternoon, Ben.

I apologize for not providing enough information... I was just trying
to minimize my post but I guess I minimized it too much. :)

A more detailed block of code is:

bool result;
bool GetByte( char *theAddr, int theCount, char *theDest);

char Dest[ SOME_SIZE ];

result =
GetByte( ( char * ) &( ( struct aStruct * ) 0 )->Addr,
1, ( char * ) ( &Dest ) );

My question is... does the struct aStruct live at address 0, or does
address 0 contain a pointer to wherever aStruct actually lives? Seems
to me like the "&" ahead of ( ( struct aStruct * ) 0 )->Addr would
indicate the latter...

&((struct aStruct *)0)->Addr is the address where s.Addr would be
located, if s were located at address 0. (Note that -> binds more
tightly than &.) On many machines this has the same numerical value as
offsetof(struct aStruct, Addr), and on such machines the offsetof()
macro might be implemented in this way. According to standard C,
however, this behavior can't be relied upon.

The point of offsetof(), of course, is to tell you where a certain
member is located with respect to the starting address of a struct. So given

struct foo { T1 a; T2 b; T3 c; } s;

then

(T2 *)(((char *)s) + offsetof(struct foo, b)) == &s.b

This is sometimes useful to allow an external function to modify
something inside a structure that it doesn't otherwise know anything
about.

Now back in your case, why the program would take that address, cast it
to char *, and pass it to a function, I don't know. I expect there is
non-portable platform-specific magic going on, and you might find better
information in a group dedicated to that platform. (Is this some sort
of embedded system?)
 
K

Keith Thompson

But (type *)0 is always a null-pointer constant: if null pointers have
some odd representation, the compiler must recognise the construct and
generate that odd representation.
[...]

Quibble: (type*)0 isn't a null pointer constant, unless "type" is
void. But 0 is a null pointer constant, and a null pointer constant
converted to a pointer type is guaranteed to yield a null pointer of
that type.

The language could just as easily defined the term "null pointer
constant" to include (type*)0 for any type "type", so this isn't a
huge deal.
 
E

Eric

We don't know what GetByte() will do with that pointer - you haven't told
us what GetByte() does. I would guess that this call to GetBytes()
copies 1 byte from that location into Dest.

I don't know what it does, either. I'm assuming that it does as you
speculate... copies 1 byte into Dest.

To answer Nate's question... yes, it's an embedded system, uses an
ARM7-based controller. There is quite a bit of, as you say,
non-portable platform-specific magic going on. I could, as you
suggest, find an ARM7 group and ask there, but mostly I'm just asking
from a standards-based perspective.

Thanks to all for your help...
 
B

Ben Bacarisse

pete said:
Keith said:
But (type *)0 is always a null-pointer constant: if null pointers have
some odd representation, the compiler must recognise the construct and
generate that odd representation.
[...]

Quibble: (type*)0 isn't a null pointer constant, unless "type" is
void. But 0 is a null pointer constant, and a null pointer constant
converted to a pointer type is guaranteed to yield a null pointer of
that type.

The language could just as easily defined the term "null pointer
constant" to include (type*)0 for any type "type", so this isn't a
huge deal.

That would make the value of the following expression, become ambiguous:

sizeof(1 ? (char)0 : (char *)0))

Lets get rid of the extra ) first:

sizeof(1 ? (char)0 : (char *)0)

I don't see the problem. The expression in the sizeof has type char *
(by current rules). Under Keith's new rule both the 2nd and 3rd
operand of the conditional expression would be null pointer constants
so the result would be of type void *. It is a change, but I don't see
anything ambiguous about it.
 
K

Keith Thompson

Ben Bacarisse said:
pete said:
Keith said:
(e-mail address removed) (Richard Tobin) writes:
[...]
But (type *)0 is always a null-pointer constant: if null pointers have
some odd representation, the compiler must recognise the construct and
generate that odd representation.
[...]

Quibble: (type*)0 isn't a null pointer constant, unless "type" is
void. But 0 is a null pointer constant, and a null pointer constant
converted to a pointer type is guaranteed to yield a null pointer of
that type.

The language could just as easily defined the term "null pointer
constant" to include (type*)0 for any type "type", so this isn't a
huge deal.

That would make the value of the following expression, become ambiguous:

sizeof(1 ? (char)0 : (char *)0))

Lets get rid of the extra ) first:

sizeof(1 ? (char)0 : (char *)0)

I don't see the problem. The expression in the sizeof has type char *
(by current rules). Under Keith's new rule both the 2nd and 3rd
operand of the conditional expression would be null pointer constants
so the result would be of type void *. It is a change, but I don't see
anything ambiguous about it.

How do you get void*?

(char)0 is a null pointer constant (and is of type char); (char *)0
would be a null pointer constant under my new rule (and would be of
type char*).

The current wording does become confusing with my new rule. C99
6.5.15p6:

If both the second and third operands are pointers or one is a
null pointer constant and the other is a pointer,

Both are null pointer constants, but only the third operand is a
pointer, so we treat (char)0 as "one" and (char *)0 as "the other.

the result type is a pointer to a type qualified with all the type
qualifiers of the types pointed-to by both operands.

so I *think* the result of the conditional operator would
unambiguously be char*. But I could easily be mistaken.

But this would be a real problem:

sizeof (1 ? (int*)0 : (char*)0)

Currently, a null pointer constant cannot be of a pointer type other
than void* (though it can be of any integral type). With my new rule,
a null pointer constant can be of any pointer type, and the definition
of the conditional operator would have to be changed accordingly.

This shouldn't be a huge deal, both because such a change would be
easy enough to make, and because I wasn't particularly serious about
my proposed new rule anyway.
 
B

Ben Bacarisse

Keith Thompson said:
Ben Bacarisse said:
pete said:
Keith Thompson wrote:
(e-mail address removed) (Richard Tobin) writes:
[...]
But (type *)0 is always a null-pointer constant: if null pointers have
some odd representation, the compiler must recognise the construct and
generate that odd representation.
[...]

Quibble: (type*)0 isn't a null pointer constant, unless "type" is
void. But 0 is a null pointer constant, and a null pointer constant
converted to a pointer type is guaranteed to yield a null pointer of
that type.

The language could just as easily defined the term "null pointer
constant" to include (type*)0 for any type "type", so this isn't a
huge deal.

That would make the value of the following expression, become ambiguous:

sizeof(1 ? (char)0 : (char *)0))

Lets get rid of the extra ) first:

sizeof(1 ? (char)0 : (char *)0)

I don't see the problem. The expression in the sizeof has type char *
(by current rules). Under Keith's new rule both the 2nd and 3rd
operand of the conditional expression would be null pointer constants
so the result would be of type void *. It is a change, but I don't see
anything ambiguous about it.

How do you get void*?

I am not sure any more. I think I just made it up! Honestly, I think
I was just extending the rules governing the conditional operator to
say that when there are two null pointer constants, the result would
be void * since this is a sort of universal object pointer type,
particularly when the value being carried is null.
(char)0 is a null pointer constant (and is of type char); (char *)0
would be a null pointer constant under my new rule (and would be of
type char*).

The current wording does become confusing with my new rule. C99
6.5.15p6:

If both the second and third operands are pointers or one is a
null pointer constant and the other is a pointer,

Both are null pointer constants, but only the third operand is a
pointer, so we treat (char)0 as "one" and (char *)0 as "the other.

the result type is a pointer to a type qualified with all the type
qualifiers of the types pointed-to by both operands.

This bit does not say what the result type is -- only how it is
qualified. I had to read it several times before I saw this detail
but I think I have not misread it. I think the result type comes from
this bit further on:

if one operand is a null pointer constant, the result has the
type of the other operand;
so I *think* the result of the conditional operator would
unambiguously be char*. But I could easily be mistaken.

So char is also possible. It could be argued that there is a sort of
binding of "one" and "the other" from the start of p6 to this later
phrase but I see that as a bit of a stretch. I think this is the
ambiguity that pete was talking about.
But this would be a real problem:

sizeof (1 ? (int*)0 : (char*)0)

Currently, a null pointer constant cannot be of a pointers type other
than void* (though it can be of any integral type). With my new rule,
a null pointer constant can be of any pointer type, and the definition
of the conditional operator would have to be changed accordingly.

My invented rule covers this case of course. That does not give it
any merit in this context since I was trying to explain another
example, and doing so using extra invented rules is not helpful!
 

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