473,473 Members | 2,044 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

struct named 0


Is the following code conformat to ANSI C?

typedef struct {
int a;
int b;
} doomdata;

int main(void)
{
int x;

x = (int)&((doomdata*)0)->b;
printf("x=%d\n", x);
return x;
}

The part,
(int)&((doomdata*)0)->b;

is it conformant to ANSI C? What is it supposed to do?

Thanks for any tips.

Napi

--
http://www.axiomsol.com
http://www.cs.indiana.edu/hyplan/napi.html
Nov 14 '05 #1
60 2866
Mohd Hanafiah Abdullah wrote:
Is the following code conformat to ANSI C?

typedef struct {
int a;
int b;
} doomdata;

int main(void)
{
int x;

x = (int)&((doomdata*)0)->b;
printf("x=%d\n", x);
return x;
}

The part,
(int)&((doomdata*)0)->b;

is it conformant to ANSI C? What is it supposed to do?


According to prior discussions, it is at least shady but
probably not conformant, depending on whether the address
0 is dereferenced or not.
This is a version of the offsetof macro from <stddef.h>:
offsetof(doomdata,b)
gives you the byte offset of b in struct doomsdata, that means
if we have
struct doomdata d;
unsigned char *p = (unsigned char *)&d;
then the address of d.b is p+offsetof(doomdata,b).
In contrast to offsetof which expands to an expression of
type size_t, the above will give you an int.

Use offsetof.
Cheers
Michael
--
E-Mail: Mine is an /at/ gmx /dot/ de address.
Nov 14 '05 #2
Mohd Hanafiah Abdullah <na**@cs.indiana.edu> wrote:
typedef struct {
int a;
int b;
} doomdata; (int)&((doomdata*)0)->b; is it conformant to ANSI C?


IMO no, because it doesn't point to any valid object, so
invokes UB.

Question to others:
Would this be correct?
(int)&((doomdata*)0)->a;

--
Stan Tobias
mailx `echo si***@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 14 '05 #3

"Mohd Hanafiah Abdullah" <na**@cs.indiana.edu> wrote

The part,
(int)&((doomdata*)0)->b;

is it conformant to ANSI C? What is it supposed to do?

This is the offsetof() macro, which calculates the offset of a structure
member relative to the struct origin.
On most platforms it works, but it suffers a few problems on unusual
systems. For instance, if NULL is not all bits zero, or if there are trap
pointer representations, then the method won't work naturally. So I believe
that in C89 it is not required to work.
Nov 14 '05 #4
Michael Mair <Mi**********@invalid.invalid> scribbled the following:
Mohd Hanafiah Abdullah wrote:
Is the following code conformat to ANSI C?

typedef struct {
int a;
int b;
} doomdata;

int main(void)
{
int x;

x = (int)&((doomdata*)0)->b;
printf("x=%d\n", x);
return x;
}

The part,
(int)&((doomdata*)0)->b;

is it conformant to ANSI C? What is it supposed to do?
According to prior discussions, it is at least shady but
probably not conformant, depending on whether the address
0 is dereferenced or not.
This is a version of the offsetof macro from <stddef.h>:
offsetof(doomdata,b)
gives you the byte offset of b in struct doomsdata, that means
if we have
struct doomdata d;
unsigned char *p = (unsigned char *)&d;
then the address of d.b is p+offsetof(doomdata,b).
In contrast to offsetof which expands to an expression of
type size_t, the above will give you an int. Use offsetof.


To be more specific, offsetof may be defined as the version mentioned by
the OP, but it doesn't have to be. However, whatever offsetof is defined
as, it is guaranteed to always be legal and valid code for that
particular implementation. The version mentioned by the OP may or may
not be valid and legal code, depending on the implementation.

--
/-- Joona Palaste (pa*****@cc.helsinki.fi) ------------- Finland --------\
\-------------------------------------------------------- rules! --------/
"The trouble with the French is they don't have a word for entrepreneur."
- George Bush
Nov 14 '05 #5
On Sun, 28 Nov 2004 11:26:06 +0100, Michael Mair
<Mi**********@invalid.invalid> wrote in comp.lang.c:
Mohd Hanafiah Abdullah wrote:
Is the following code conformat to ANSI C?

typedef struct {
int a;
int b;
} doomdata;

int main(void)
{
int x;

x = (int)&((doomdata*)0)->b;
printf("x=%d\n", x);
return x;
}

The part,
(int)&((doomdata*)0)->b;

is it conformant to ANSI C? What is it supposed to do?
According to prior discussions, it is at least shady but
probably not conformant, depending on whether the address
0 is dereferenced or not.


No it has undefined behavior, and has nothing to do with the "address
0", but with a null pointer. C is defined in terms of an abstract
machine, and in the abstract machine this expression dereferences a
null pointer.
This is a version of the offsetof macro from <stddef.h>:
offsetof(doomdata,b)
gives you the byte offset of b in struct doomsdata, that means
if we have
struct doomdata d;
unsigned char *p = (unsigned char *)&d;
then the address of d.b is p+offsetof(doomdata,b).
In contrast to offsetof which expands to an expression of
type size_t, the above will give you an int.

Use offsetof.
Yes, indeed. The implementation's offsetof() macro might indeed
contain similar code, excepting the cast to int. But the
implementation is not constrained to follow the rules of the abstract
machine, only user programs are.
Cheers
Michael


--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #6
On 28 Nov 2004 10:41:21 GMT, "S.Tobias"
<si***@FamOuS.BedBuG.pAlS.INVALID> wrote in comp.lang.c:
Mohd Hanafiah Abdullah <na**@cs.indiana.edu> wrote:
typedef struct {
int a;
int b;
} doomdata;

(int)&((doomdata*)0)->b;

is it conformant to ANSI C?


IMO no, because it doesn't point to any valid object, so
invokes UB.

Question to others:
Would this be correct?
(int)&((doomdata*)0)->a;


Technically it is still undefined behavior, as the semantics of the
expression dereference a null pointer.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #7
On Sun, 28 Nov 2004 10:53:23 -0000, "Malcolm"
<ma*****@55bank.freeserve.co.uk> wrote in comp.lang.c:

"Mohd Hanafiah Abdullah" <na**@cs.indiana.edu> wrote

The part,
(int)&((doomdata*)0)->b;

is it conformant to ANSI C? What is it supposed to do?
This is the offsetof() macro, which calculates the offset of a structure
member relative to the struct origin.


No, it can't be the offsetof() macro. This expression specifically
yields an int, whereas the offsetof() macro yields a size_t. This
expression yields undefined behavior.
On most platforms it works, but it suffers a few problems on unusual
systems. For instance, if NULL is not all bits zero, or if there are trap
pointer representations, then the method won't work naturally. So I believe
that in C89 it is not required to work.


When will people get it through their heads that it makes not a whit
of difference whether all bits zero happens to be a representation of
a null pointer on a particular platform?

The correspondence between an integer constant expression evaluating
to 0 and a null pointer is exactly the same as quite a few other
constant expressions, that is something that is evaluated and
substituted at compile-time and has nothing to do with run-time
values.

The conversion of a quoted string to an array of '\0' terminated
values is a compile-time conversion.

The conversion of escape sequences like '\n' and '\t' to their single
character equivalents in string or character constants is a
compile-time conversion.

The conversion of an integer constant expression evaluating to 0, or
such an expression cast to the type pointer-to-void, to a null pointer
constant is a compile-time conversion, not a run-time one.

If the one and only representation for a null pointer in a particular
implementation is 0xDEADBEEF, and such a value is returned by a call
to fopen(), for example, a comparison of that value against NULL or 0
will still be true.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #8
On Sun, 28 Nov 2004 08:50:39 +0000 (UTC), na**@cs.indiana.edu (Mohd
Hanafiah Abdullah) wrote in comp.lang.c:

Is the following code conformat to ANSI C?

typedef struct {
int a;
int b;
} doomdata;

int main(void)
{
int x;

x = (int)&((doomdata*)0)->b;
No, the line above invokes undefined behavior, because it dereferences
a null pointer.
printf("x=%d\n", x);
return x;
}

The part,
(int)&((doomdata*)0)->b;

is it conformant to ANSI C? What is it supposed to do?
No, it is most certainly not conforming. It might have been written
as an example, or it might have been written by someone who doesn't
know that the offsetof(struct_type,member_name) macro defined in
<stddef.h> does the same thing in a conforming manner.
Thanks for any tips.

Napi


--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #9

"Jack Klein" <ja*******@spamcop.net> wrote
The part,
(int)&((doomdata*)0)->b;

is it conformant to ANSI C? What is it supposed to do?
This is the offsetof() macro, which calculates the offset of a structure
member relative to the struct origin.


No, it can't be the offsetof() macro. This expression specifically
yields an int, whereas the offsetof() macro yields a size_t. This
expression yields undefined behavior.

This is true. int is not guaranteed to be big enough to hold the offset of a
structure element (!).
When will people get it through their heads that it makes not a whit
of difference whether all bits zero happens to be a representation of
a null pointer on a particular platform?

Unfortunately it does make a difference.
For instance consider

char **list = calloc(N, sizeof(char *));

for(i=0;i<N;i++)
if( someconditon() )
list[i] = malloc(10);

for(i=0;i<N;i++)
free(list[i]);

Also consider if a null pointer is cast to an integral type, or if a pointer
derived by adding an offset to the null pointer is cast to an integer. This
cast is a simple bitwise conversion, so results will differ on a platform on
which NULL is not all bits zero.

However char *ptr = 0; will always set ptr to NULL, regardless of whether
the null pointer is all bits zero. Here you are correct.

Nov 14 '05 #10
Mohd Hanafiah Abdullah wrote:

The part,
(int)&((doomdata*)0)->b;

is it conformant to ANSI C? What is it supposed to do?

#include <stdio.h>
#include <stddef.h>

typedef struct
{
int a;
int b;
} doomdata;

int main(void)
{
int x;
#if 0
/* mha: the following is an attempt to mimic offsetof on an
implementation not having offsetof in <stddef.h> */
x = (int) &((doomdata *) 0)->b;
#endif
x = offsetof(doomdata, b);
printf("x=%d\n", x);

return 0; /* mha: returning x, when x is not one
of 0, EXIT_SUCCESS, or EXIT_FAILURE
is at best implementation-defined */
}
Nov 14 '05 #11
Jack Klein <ja*******@spamcop.net> wrote:
On 28 Nov 2004 10:41:21 GMT, "S.Tobias"
<si***@FamOuS.BedBuG.pAlS.INVALID> wrote in comp.lang.c:
Mohd Hanafiah Abdullah <na**@cs.indiana.edu> wrote:
typedef struct {
int a;
int b;
} doomdata;
Would this be correct?
(int)&((doomdata*)0)->a;
Technically it is still undefined behavior, as the semantics of the
expression dereference a null pointer.


(This deserves a separate thread, but since I asked the above
question here, I'll continue here too.)

As I understand the expression constitutes an access to the structure
member.

1. Does a member access constitute an access to the *whole* structure?
eg.:
struct A { int i; int _i; };
struct B { int i; float f; };
struct A a = {0};
struct B *pb = (struct B*)&a;
pb->i; //UB?
(*pb).i; //UB?
Do I access the first int sub-object in `a' only, or do I access
the whole object `a'?

2. I see certain similarity between structs and arrays (in fact,
both are called "aggregates").
Why is it that for array:
&a[5];
doesn't constitute object access (6.5.3.2#3), whereas for struct:
&s.m;
&ps->m;
the expressions do constitute access?
Why is the language designed like this?

--
Stan Tobias
mailx `echo si***@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 14 '05 #12
"Jack Klein" <ja*******@spamcop.net> wrote in message
news:ov********************************@4ax.com...
On Sun, 28 Nov 2004 11:26:06 +0100, Michael Mair
<Mi**********@invalid.invalid> wrote in comp.lang.c:
Mohd Hanafiah Abdullah wrote:
Is the following code conformat to ANSI C?

typedef struct {
int a;
int b;
} doomdata;

int main(void)
{
int x;

x = (int)&((doomdata*)0)->b;
printf("x=%d\n", x);
return x;
}

The part,
(int)&((doomdata*)0)->b;

is it conformant to ANSI C? What is it supposed to do?


According to prior discussions, it is at least shady but
probably not conformant, depending on whether the address
0 is dereferenced or not.


No it has undefined behavior, and has nothing to do with the "address
0", but with a null pointer. C is defined in terms of an abstract
machine, and in the abstract machine this expression dereferences a
null pointer.

/snip/

It does not dereference any pointer. The & cancels out
the ->.

btw: If the dereference actually happened, how could the
compiler figure out the address of an int that was produced
by the dereference?

Nov 14 '05 #13
In article <co**********@news7.svr.pol.co.uk>,
"Malcolm" <ma*****@55bank.freeserve.co.uk> wrote:
Also consider if a null pointer is cast to an integral type, or if a pointer
derived by adding an offset to the null pointer is cast to an integer. This
cast is a simple bitwise conversion, so results will differ on a platform on
which NULL is not all bits zero.


Not quite; a conversion from a pointer type to an integer does whatever
the implementation thinks is a good idea; usually the bits of the
representation are copied unchanged, but that is not necessarily so.

In C99, there seems to be an actual requirement that casting an integer
zero to a pointer type will produce a null pointer. If null pointers
don't have all bits zero, and an integer 0 has all bits zero, and
converting an integer 0 to a pointer produces a null pointer, then logic
says that this conversion cannot leave the bits unchanged.
Nov 14 '05 #14
"Christian Bau" <ch***********@cbau.freeserve.co.uk> wrote

In C99, there seems to be an actual requirement that casting an integer
zero to a pointer type will produce a null pointer. If null pointers
don't have all bits zero, and an integer 0 has all bits zero, and
converting an integer 0 to a pointer produces a null pointer, then logic
says that this conversion cannot leave the bits unchanged.

But in this case we are not casting an integer 0 to a pointer, but a null
pointer to an integer.
Nov 14 '05 #15

"xarax" <xa***@email.com> wrote

It does not dereference any pointer. The & cancels out
the ->.

However it is illegal even to load a non-valid pointer, except NULL or
course, into a pointer variable.
One reason is that some platforms have hardware traps that trigger an error
when illegal values are loaded into address registers. Another reason is
that sometimes with segmented architectures you can get strange results. For
instance if char *ptr points to the beginning of a segment, then ptr - 1
might compare to greater than ptr. The address of NULL + a few bytes is an
illegal value, and I don't believe that the cast to an int makes the
expression a legal one, though I am open to correction on this.
Nov 14 '05 #16
On 28 Nov 2004 20:29:37 GMT, "S.Tobias"
<si***@FamOuS.BedBuG.pAlS.INVALID> wrote in comp.lang.c:
Jack Klein <ja*******@spamcop.net> wrote:
On 28 Nov 2004 10:41:21 GMT, "S.Tobias"
<si***@FamOuS.BedBuG.pAlS.INVALID> wrote in comp.lang.c:
Mohd Hanafiah Abdullah <na**@cs.indiana.edu> wrote: typedef struct {
> int a;
> int b;
> } doomdata; Would this be correct?
(int)&((doomdata*)0)->a;
Technically it is still undefined behavior, as the semantics of the
expression dereference a null pointer.


(This deserves a separate thread, but since I asked the above
question here, I'll continue here too.)

As I understand the expression constitutes an access to the structure
member.

1. Does a member access constitute an access to the *whole* structure?
eg.:


The term 'access' is really only used in the C standard in conjunction
with the volatile qualifier, where the wording is unfortunately vague
enough that it can be construed several different ways.
struct A { int i; int _i; };
struct B { int i; float f; };
struct A a = {0};
struct B *pb = (struct B*)&a;
pb->i; //UB?
(*pb).i; //UB?
The two statements above actually have defined behavior, but not for
the reason you might think. The language guarantees that a pointer to
structure, suitably cast, is also a pointer to its first member. So
in given that 'pb' holds any of the following:

- a pointer to any structure type whose first member is an int

- a pointer to an array of ints

- a pointer to a single int

....and the int pointed to has a valid value, the expressions, though
not recommended, will work as designed. The compiler must generate
code equivalent to *(int *)pb, and if there is actually an int there
all is well.
Do I access the first int sub-object in `a' only, or do I access
the whole object `a'?
Now that depends what you mean by access. Let's assume a Pentium
(1/2/3/4) platform with a typical compiler, which means the size of
either of your structures is 8 8-bit bytes. Now let's also assume
that this implementation allocates all structures on an address evenly
divisible by 8, a not uncommon performance feature of such
implementations.

With the assumptions above, if the processor needs to access the
structure from memory it will perform a 64-bit access physically, so
even though your code does not direct the abstract machine to touch
the value of other members in any way, the entire memory space holding
the structure will be physically read.
2. I see certain similarity between structs and arrays (in fact,
both are called "aggregates").
Why is it that for array:
&a[5];
doesn't constitute object access (6.5.3.2#3), whereas for struct:
&s.m;
&ps->m;
the expressions do constitute access?
Why is the language designed like this?


There are actually more differences than similarities between structs
and arrays, despite the fact that both are aggregates. Structs are
first class objects, meaning they can be assigned, passed to and
returned from functions by value, and their names are never implicitly
converted to pointers. Arrays are not first class objects and do not
share any of the characteristics above.

As for other differences in this particular case, this is spelled out
by paragraph 3 of 6.5.3.2 of C99:

[begin quotation]
The unary & operator returns the address of its operand. If the
operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If
the operand is the result of a unary * operator, neither that operator
nor the & operator is evaluated and the result is as if both were
omitted, except that the constraints on the operators still apply and
the result is not an lvalue. Similarly, if the operand is the result
of a [] operator, neither the & operator nor the unary * that is
implied by the [] is evaluated and the result is as if the & operator
were removed and the [] operator were changed to a + operator.
Otherwise, the result is a pointer to the object or function
designated by its operand.
[end quotation]

Note the differences between applying '&' to the result of a '*'
operator and to the result of a '[]' operator. In the former case,
neither '&' nor '*' are evaluated as such, but note "the constraints
on the operators still apply".

Now let's back up to paragraph 1 of 6.5.3.2, which lists the
constraints for the unary '&' operator:

[begin quotation]
The operand of the unary & operator shall be either a function
designator, the result of a [] or unary * operator, or an lvalue that
designates an object that is not a bit-field and is not declared with
the register storage-class specifier.
[end quotation]

Notice that the expression under discussion,

(int)&((doomdata*)0)->a;

....is none of these things. Specifically, the operand of the '&'
operator, '((doomdata*)0)->a' is:

- not a function designator

- not the result of a [] operator

- not the result of a unary * operator

- and, because of the null pointer, not an lvalue

Finally consider one last thing, namely that regardless of whether
there is an actual access to an object, the expression explicitly
performs pointer arithmetic on a null pointer, and such use of a null
pointer is undefined in and of itself.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #17
On Sun, 28 Nov 2004 22:17:21 +0000, Christian Bau
<ch***********@cbau.freeserve.co.uk> wrote in comp.lang.c:
In article <co**********@news7.svr.pol.co.uk>,
"Malcolm" <ma*****@55bank.freeserve.co.uk> wrote:
Also consider if a null pointer is cast to an integral type, or if a pointer
derived by adding an offset to the null pointer is cast to an integer. This
cast is a simple bitwise conversion, so results will differ on a platform on
which NULL is not all bits zero.


Not quite; a conversion from a pointer type to an integer does whatever
the implementation thinks is a good idea; usually the bits of the
representation are copied unchanged, but that is not necessarily so.

In C99, there seems to be an actual requirement that casting an integer
zero to a pointer type will produce a null pointer. If null pointers
don't have all bits zero, and an integer 0 has all bits zero, and
converting an integer 0 to a pointer produces a null pointer, then logic
says that this conversion cannot leave the bits unchanged.


I am unsure of your meaning here. Do you mean an integral constant at
the source level, as in:

double *dp = (double * 0);

....or do you actually mean the value of an integer object, as in:

int x = 0;
double *dp = (double *x);

If you mean the latter, I have never noticed anything in C99 requiring
the result be a null pointer.

Could you please clarify and include chapter & verse?

Thanks.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #18
On Sun, 28 Nov 2004 19:40:02 -0000, "Malcolm"
<ma*****@55bank.freeserve.co.uk> wrote in comp.lang.c:

"Jack Klein" <ja*******@spamcop.net> wrote
> The part,
> (int)&((doomdata*)0)->b;
>
> is it conformant to ANSI C? What is it supposed to do?
>
This is the offsetof() macro, which calculates the offset of a structure
member relative to the struct origin.
No, it can't be the offsetof() macro. This expression specifically
yields an int, whereas the offsetof() macro yields a size_t. This
expression yields undefined behavior.

This is true. int is not guaranteed to be big enough to hold the offset of a
structure element (!).

When will people get it through their heads that it makes not a whit
of difference whether all bits zero happens to be a representation of
a null pointer on a particular platform?

Unfortunately it does make a difference.
For instance consider

char **list = calloc(N, sizeof(char *));


What is the above supposed to prove? It most specifically does not
guarantee to set the N pointers in list to NULL. Setting all bits to
0 with calloc() or memset() is a run-time operation and has nothing at
all to do with the compiler-time conversion of constant expressions to
null pointer constants.
for(i=0;i<N;i++)
if( someconditon() )
list[i] = malloc(10);

for(i=0;i<N;i++)
free(list[i]);
This code is badly broken, what is the point?
Also consider if a null pointer is cast to an integral type, or if a pointer
derived by adding an offset to the null pointer is cast to an integer. This
cast is a simple bitwise conversion, so results will differ on a platform on
which NULL is not all bits zero.
Again, what is your point. Casting a pointer, null or not, to an
integral type, is defined if and only if there is an integral type
large enough, but let's assume and bypass that. The operation of the
cast is completely implementation-defined, thus automatically neither
portable nor strictly conforming. If you do such a thing, it is up to
you to take such implementation-defined behavior into account as a
part of anything and everything you do with those integers.

As for adding an offset to a null pointer, that is undefined period.
However char *ptr = 0; will always set ptr to NULL, regardless of whether
the null pointer is all bits zero. Here you are correct.


Yes, because this is a compile-time conversion, which happens during
source code translation. This is no more or less remarkable than the
fact that:

char a1 [3] = "\n";
char a2 [3];
a2[0] = '\\';
a2[1] = 'n';
a2[3] = '\o';

....will produce two strings that will not test equal under any string
or memory comparison function.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #19

"Malcolm" <ma*****@55bank.freeserve.co.uk> wrote in message
news:co**********@newsg4.svr.pol.co.uk...

"xarax" <xa***@email.com> wrote

It does not dereference any pointer. The & cancels out
the ->.

However it is illegal even to load a non-valid pointer, except NULL or
course, into a pointer variable.
One reason is that some platforms have hardware traps that trigger an error
when illegal values are loaded into address registers. Another reason is
that sometimes with segmented architectures you can get strange results. For
instance if char *ptr points to the beginning of a segment, then ptr - 1
might compare to greater than ptr. The address of NULL + a few bytes is an
illegal value, and I don't believe that the cast to an int makes the
expression a legal one, though I am open to correction on this.


Notwithstanding what you wrote, the compiler will not
generate a load into an address register, because the
& cancels out the ->. The compiler has all the information
it needs to resolve the expression to a constant offset
value.
Nov 14 '05 #20

"Malcolm" <ma*****@55bank.freeserve.co.uk> wrote in message
news:co**********@newsg3.svr.pol.co.uk...
"Christian Bau" <ch***********@cbau.freeserve.co.uk> wrote

In C99, there seems to be an actual requirement that casting an integer
zero to a pointer type will produce a null pointer. If null pointers
don't have all bits zero, and an integer 0 has all bits zero, and
converting an integer 0 to a pointer produces a null pointer, then logic
says that this conversion cannot leave the bits unchanged.

But in this case we are not casting an integer 0 to a pointer, but a null
pointer to an integer.


No, you're not.

You casting integer 0 into a null pointer, offsetting
that pointer by the distance to the member field, then
taking the address of that field (which converts the
pointer back to an integer -- that reverses whatever
happened in converting the integer to a pointer). That
leaves a simple integer constant that can be resolved
at compile time.

The & cancels out the ->. There is no load of a pointer
into an address register whatsoever and no runtime
dereference.

Nov 14 '05 #21
"Malcolm" <ma*****@55bank.freeserve.co.uk> wrote:
"Jack Klein" <ja*******@spamcop.net> wrote


When will people get it through their heads that it makes not a whit
of difference whether all bits zero happens to be a representation of
a null pointer on a particular platform?

Also consider if a null pointer is cast to an integral type, or if a pointer
derived by adding an offset to the null pointer is cast to an integer. This
cast is a simple bitwise conversion, so results will differ on a platform on
which NULL is not all bits zero.


Actually the cast is implementation-defined (and the implementation
can define it to be UB in some, or all, cases).
It would make sense for an implementation to give 0 for
(int)(void *)0, even if (void *)0 were not all bits zero.

The expression (int)&((doomdata*)0)->b is relying on 2 non-standard
things:
- the compiler doesn't actually dereference 0
- the cast to int works as if pointers are ints in a flat
memory model and (int)NULL == 0

It's irrelevant to this expression whether NULL is all-bits-zero
or not.
Nov 14 '05 #22

"Jack Klein" <ja*******@spamcop.net> wrote in message
news:bn********************************@4ax.com...
On 28 Nov 2004 20:29:37 GMT, "S.Tobias"
<si***@FamOuS.BedBuG.pAlS.INVALID> wrote in comp.lang.c:
Jack Klein <ja*******@spamcop.net> wrote:
On 28 Nov 2004 10:41:21 GMT, "S.Tobias"
<si***@FamOuS.BedBuG.pAlS.INVALID> wrote in comp.lang.c:
> Mohd Hanafiah Abdullah <na**@cs.indiana.edu> wrote:
> > typedef struct {
> > int a;
> > int b;
> > } doomdata;

> Would this be correct?
> (int)&((doomdata*)0)->a;

Technically it is still undefined behavior, as the semantics of the
expression dereference a null pointer.


(This deserves a separate thread, but since I asked the above
question here, I'll continue here too.)

As I understand the expression constitutes an access to the structure
member.

1. Does a member access constitute an access to the *whole* structure?
eg.:


The term 'access' is really only used in the C standard in conjunction
with the volatile qualifier, where the wording is unfortunately vague
enough that it can be construed several different ways.
struct A { int i; int _i; };
struct B { int i; float f; };
struct A a = {0};
struct B *pb = (struct B*)&a;
pb->i; //UB?
(*pb).i; //UB?


The two statements above actually have defined behavior, but not for
the reason you might think. The language guarantees that a pointer to
structure, suitably cast, is also a pointer to its first member. So
in given that 'pb' holds any of the following:

- a pointer to any structure type whose first member is an int

- a pointer to an array of ints

- a pointer to a single int

...and the int pointed to has a valid value, the expressions, though
not recommended, will work as designed. The compiler must generate
code equivalent to *(int *)pb, and if there is actually an int there
all is well.
Do I access the first int sub-object in `a' only, or do I access
the whole object `a'?


Now that depends what you mean by access. Let's assume a Pentium
(1/2/3/4) platform with a typical compiler, which means the size of
either of your structures is 8 8-bit bytes. Now let's also assume
that this implementation allocates all structures on an address evenly
divisible by 8, a not uncommon performance feature of such
implementations.

With the assumptions above, if the processor needs to access the
structure from memory it will perform a 64-bit access physically, so
even though your code does not direct the abstract machine to touch
the value of other members in any way, the entire memory space holding
the structure will be physically read.
2. I see certain similarity between structs and arrays (in fact,
both are called "aggregates").
Why is it that for array:
&a[5];
doesn't constitute object access (6.5.3.2#3), whereas for struct:
&s.m;
&ps->m;
the expressions do constitute access?
Why is the language designed like this?


There are actually more differences than similarities between structs
and arrays, despite the fact that both are aggregates. Structs are
first class objects, meaning they can be assigned, passed to and
returned from functions by value, and their names are never implicitly
converted to pointers. Arrays are not first class objects and do not
share any of the characteristics above.

As for other differences in this particular case, this is spelled out
by paragraph 3 of 6.5.3.2 of C99:

[begin quotation]
The unary & operator returns the address of its operand. If the
operand has type ''type'', the result has type ''pointer to type''. If
the operand is the result of a unary * operator, neither that operator
nor the & operator is evaluated and the result is as if both were
omitted, except that the constraints on the operators still apply and
the result is not an lvalue. Similarly, if the operand is the result
of a [] operator, neither the & operator nor the unary * that is
implied by the [] is evaluated and the result is as if the & operator
were removed and the [] operator were changed to a + operator.
Otherwise, the result is a pointer to the object or function
designated by its operand.
[end quotation]

Note the differences between applying '&' to the result of a '*'
operator and to the result of a '[]' operator. In the former case,
neither '&' nor '*' are evaluated as such, but note "the constraints
on the operators still apply".

Now let's back up to paragraph 1 of 6.5.3.2, which lists the
constraints for the unary '&' operator:

[begin quotation]
The operand of the unary & operator shall be either a function
designator, the result of a [] or unary * operator, or an lvalue that
designates an object that is not a bit-field and is not declared with
the register storage-class specifier.
[end quotation]

Notice that the expression under discussion,

(int)&((doomdata*)0)->a;

...is none of these things. Specifically, the operand of the '&'
operator, '((doomdata*)0)->a' is:

- not a function designator

- not the result of a [] operator

- not the result of a unary * operator


Yes it is unary *. a->b is an alias for (*a).b
- and, because of the null pointer, not an lvalue

Finally consider one last thing, namely that regardless of whether
there is an actual access to an object, the expression explicitly
performs pointer arithmetic on a null pointer, and such use of a null
pointer is undefined in and of itself.


Therefore, & cancels out the -> to yield a simple
integer constant that is resolved at compile time.
Nov 14 '05 #23
In article <q1*****************@newsread3.news.pas.earthlink. net>
xarax <xa***@email.com> wrote:
... the compiler will not generate a load into an address register,
because the & cancels out the ->. The compiler has all the information
it needs to resolve the expression to a constant offset value.


There is only one problem: the compiler really sucks. It does
only what it is absolutely forced to by the C Standard. The C
Standard *allows* it to follow the pointer first, and only then
compute the offset, so it does.

(Well, which "the" compiler are *you* talking about?)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #24
"Chris Torek" <no****@torek.net> wrote in message
news:co*********@news2.newsguy.com...
In article <q1*****************@newsread3.news.pas.earthlink. net>
xarax <xa***@email.com> wrote:
... the compiler will not generate a load into an address register,
because the & cancels out the ->. The compiler has all the information
it needs to resolve the expression to a constant offset value.


There is only one problem: the compiler really sucks. It does
only what it is absolutely forced to by the C Standard. The C
Standard *allows* it to follow the pointer first, and only then
compute the offset, so it does.


Following the pointer into memory would make it
impossible to determine the address of the field.

The & cancels out the apparent dereference.
Nov 14 '05 #25
>> In article <q1*****************@newsread3.news.pas.earthlink. net>
xarax <xa***@email.com> wrote:
>... the compiler will not generate a load into an address register,
>because the & cancels out the ->. The compiler has all the information
>it needs to resolve the expression to a constant offset value.
"Chris Torek" <no****@torek.net> wrote in message
news:co*********@news2.newsguy.com...
There is only one problem: the compiler really sucks. It does
only what it is absolutely forced to by the C Standard. The C
Standard *allows* it to follow the pointer first, and only then
compute the offset, so it does.

In article <PU*****************@newsread3.news.pas.earthlink. net>
xarax <xa***@email.com> wrote:Following the pointer into memory would make it
impossible to determine the address of the field.
Yes. This is why the compiler throws the result away after following
the pointer.
The & cancels out the apparent dereference.


No, the "&" makes the compiler throw away the result of the dereference.

Unfortunately, by then it is too late.

I did say this compiler really sucks. But it conforms.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #26
"Chris Torek" <no****@torek.net> wrote in message
news:co*********@news2.newsguy.com...
In article <q1*****************@newsread3.news.pas.earthlink. net>
xarax <xa***@email.com> wrote:
>... the compiler will not generate a load into an address register,
>because the & cancels out the ->. The compiler has all the information
>it needs to resolve the expression to a constant offset value.

"Chris Torek" <no****@torek.net> wrote in message
news:co*********@news2.newsguy.com...
There is only one problem: the compiler really sucks. It does
only what it is absolutely forced to by the C Standard. The C
Standard *allows* it to follow the pointer first, and only then
compute the offset, so it does.


In article <PU*****************@newsread3.news.pas.earthlink. net>
xarax <xa***@email.com> wrote:
Following the pointer into memory would make it
impossible to determine the address of the field.


Yes. This is why the compiler throws the result away after following
the pointer.
The & cancels out the apparent dereference.


No, the "&" makes the compiler throw away the result of the dereference.

Unfortunately, by then it is too late.

I did say this compiler really sucks. But it conforms.


Totally ridiculous.
Nov 14 '05 #27
"xarax" <xa***@email.com> wrote:
"Malcolm" <ma*****@55bank.freeserve.co.uk> wrote:
"Christian Bau" <ch***********@cbau.freeserve.co.uk> wrote:

In C99, there seems to be an actual requirement that casting an integer
zero to a pointer type will produce a null pointer. If null pointers
don't have all bits zero, and an integer 0 has all bits zero, and
converting an integer 0 to a pointer produces a null pointer, then logic
says that this conversion cannot leave the bits unchanged.
But in this case we are not casting an integer 0 to a pointer, but a null
pointer to an integer.


No, you're not.

You casting integer 0 into a null pointer,


Casting 0 to a pointer must give a null pointer.
offsetting that pointer by the distance to the member
field, then taking the address of that field
(which converts the pointer back to an integer
That conversion is implementation-defined
-- that reverses whatever happened in converting the
integer to a pointer).
There is no requirement for that to be true. In fact
it can't be true, if there is not an exact mapping
from integers to pointers (eg. segmented architecture,
IA64, etc.)

If you're still not convinced, imagine that NULL
lives at address 0xDEADBEEF. Then &((foo *)0)->bar
might be 0xDEADC00C, for example, and when that's
converted back to an integer you might get 0xDEADC00C
still. Not a very accurate struct offset.
Furthermore, if that is outside the range of signed int
(likely on a 32-bit system) you get undefined behaviour
(again).
That leaves a simple integer constant that can be resolved
at compile time.

There is no load of a pointer into an address register
whatsoever and no runtime dereference.


For your compiler on Tuesdays, perhaps. There is nothing
(except possible sales figures..) to stop a compiler from
loading 0 to an address register and then incrementing
it, causing a hardware exception.
Nov 14 '05 #28
In article <f3********************************@4ax.com>,
Jack Klein <ja*******@spamcop.net> wrote:
On Sun, 28 Nov 2004 22:17:21 +0000, Christian Bau
<ch***********@cbau.freeserve.co.uk> wrote in comp.lang.c:
In article <co**********@news7.svr.pol.co.uk>,
"Malcolm" <ma*****@55bank.freeserve.co.uk> wrote:
Also consider if a null pointer is cast to an integral type, or if a
pointer
derived by adding an offset to the null pointer is cast to an integer.
This
cast is a simple bitwise conversion, so results will differ on a platform
on
which NULL is not all bits zero.
Not quite; a conversion from a pointer type to an integer does whatever
the implementation thinks is a good idea; usually the bits of the
representation are copied unchanged, but that is not necessarily so.

In C99, there seems to be an actual requirement that casting an integer
zero to a pointer type will produce a null pointer. If null pointers
don't have all bits zero, and an integer 0 has all bits zero, and
converting an integer 0 to a pointer produces a null pointer, then logic
says that this conversion cannot leave the bits unchanged.


I am unsure of your meaning here. Do you mean an integral constant at
the source level, as in:

double *dp = (double * 0);

(double *) 0;
...or do you actually mean the value of an integer object, as in:

int x = 0;
double *dp = (double *x);
Both.
If you mean the latter, I have never noticed anything in C99 requiring
the result be a null pointer.

Could you please clarify and include chapter & verse?


Not directly. The result of conversion from int to for example double*
is defined directly in one special case: When the int is a null pointer
constant, that is the value is 0, and it is an integer constant
expression. But since the result of a conversion only depends on the
value converted and nothing else, the result of converting _any_ int of
value 0 must always be the same. As it is a null pointer in some cases,
it must be a null pointer in all cases.
Nov 14 '05 #29
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
The result of conversion from int to for example double*
is defined directly in one special case: When the int is a null pointer
constant, that is the value is 0, and it is an integer constant
expression.
No, the value is not important here, you're misquoting the Standard.
The Standard says: "integer constant expression with the value 0",
so first you look up what is an integer constant expression, and
then you filter only those that have the value zero. Examples are:
0
0x0
0u
(1-1)
'\000'
(int)0.0
7/11
sizeof 13 - sizeof (int)
But since the result of a conversion only depends on the
value converted and nothing else, the result of converting _any_ int of
value 0 must always be the same. As it is a null pointer in some cases,
it must be a null pointer in all cases.


The Std does not say such a thing. If it wanted the value 0 to be
converted into a null pointer, it would say "integer expression with
the value 0" or just "integer value 0". Integer constant expression
is a special case that a compiler must recognize at compile-time.

Conversions between pointer and integer types are explicitly
implementation defined.

--
Stan Tobias
mailx `echo si***@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 14 '05 #30
In article <31*************@uni-berlin.de>,
"S.Tobias" <si***@FamOuS.BedBuG.pAlS.INVALID> wrote:
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
The result of conversion from int to for example double*
is defined directly in one special case: When the int is a null pointer
constant, that is the value is 0, and it is an integer constant
expression.
No, the value is not important here, you're misquoting the Standard.
The Standard says: "integer constant expression with the value 0",
so first you look up what is an integer constant expression, and
then you filter only those that have the value zero. Examples are:
0
0x0
0u
(1-1)
'\000'
(int)0.0
7/11
sizeof 13 - sizeof (int)


So you are saying that I should look at integer constant expressions
with a value of zero, instead of looking at integers with a value of
zero that are integer constant expressions???

But since the result of a conversion only depends on the
value converted and nothing else, the result of converting _any_ int of
value 0 must always be the same. As it is a null pointer in some cases,
it must be a null pointer in all cases.

The Std does not say such a thing.
The Standard says that the conversion is implementation-defined.
"Implementation-defined" implies "defined". "Defined" implies: The
implementation gives rules what the result will be if a value is
converted. The result of _any_ operation only depends on the values
involved (and of course on the definitions given by the C Standard or
the implementation) and nothing else; for example it is independent of
the representation of a value. Now add two and two together and you get
exactly what I said.
If it wanted the value 0 to be
converted into a null pointer, it would say "integer expression with
the value 0" or just "integer value 0".
That assumption is naive.
Integer constant expression
is a special case that a compiler must recognize at compile-time.
Only in order to allow an implicit conversion to an appropriate pointer
type that wouldn't be present otherwise. The conversion itself is
implementation defined, with one special case defined by the C Standard.
Conversions between pointer and integer types are explicitly
implementation defined.


As an implementation must conform to _everything_ that the C Standard
guarantees, an implementation is not absolutely free in how it can
define the result of that conversion. Whatever definition the C
implementor chooses, it must be consistent with the requirement that for
example (double *) 0 is a null pointer. If you look at the list of
situations where null pointer constants are recognised and handled
specially by the compiler, you will find that casts are not among them.
Nov 14 '05 #31
"xarax" <xa***@email.com> wrote in message news:<34*****************@newsread3.news.pas.earth link.net>...
"Malcolm" <ma*****@55bank.freeserve.co.uk> wrote in message
news:co**********@newsg3.svr.pol.co.uk...
"Christian Bau" <ch***********@cbau.freeserve.co.uk> wrote

In C99, there seems to be an actual requirement that casting an integer
zero to a pointer type will produce a null pointer. If null pointers
don't have all bits zero, and an integer 0 has all bits zero, and
converting an integer 0 to a pointer produces a null pointer, then logic
says that this conversion cannot leave the bits unchanged.

But in this case we are not casting an integer 0 to a pointer, but a null
pointer to an integer.


No, you're not.

You casting integer 0 into a null pointer, offsetting
that pointer by the distance to the member field, then
taking the address of that field (which converts the
pointer back to an integer -- that reverses whatever
happened in converting the integer to a pointer). That
leaves a simple integer constant that can be resolved
at compile time.

The & cancels out the ->. There is no load of a pointer
into an address register whatsoever and no runtime
dereference.


Instead of continually repeating this, please prove the rest of
us wrong by quoting the sections of the Standard that require
the compiler to behave as you describe, and thus prevent the
construct resulting in undefined behaviour.
Nov 14 '05 #32
>"Chris Torek" <no****@torek.net> wrote in message
news:co*********@news2.newsguy.com...
No, the "&" makes the compiler throw away the result of the dereference.
Unfortunately, by then it is too late.
I did say this compiler really sucks. But it conforms.

In article <x9****************@newsread3.news.pas.earthlink.n et>
xarax <xa***@email.com> wrote:Totally ridiculous.


Maybe so; and you can feel free to reject such compilers (I would
myself, given the option). But while there is "what I think the
Standard *should* require", there is also "what the Standard actually
requires". Sometimes they differ. (I will note here that I also
object to the C89 wording that makes &a[N] illegal even though a+N
is legal. I am not sure whether this is fixed in C99.)

One should feel free to set one's own standards to something other
than those provided by C89 or C99; but one should be aware of where
they may differ. As another example, I do not use compilers on
which only six monocase characters matter in external identifiers
-- but I know that C89 says this is all I can rely on. Any *sensible*
C environment does better, just as any sensible compiler avoids
unnecessary, wasteful pointer-following.

It is OK to require good sense, as long as you remember that the
C Standard does not. :-)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #33
On 28 Nov 2004 19:55:18 -0800, ol*****@inspire.net.nz (Old Wolf) wrote
in comp.lang.c:
"Malcolm" <ma*****@55bank.freeserve.co.uk> wrote:
"Jack Klein" <ja*******@spamcop.net> wrote


When will people get it through their heads that it makes not a whit
of difference whether all bits zero happens to be a representation of
a null pointer on a particular platform?
Also consider if a null pointer is cast to an integral type, or if a pointer
derived by adding an offset to the null pointer is cast to an integer. This
cast is a simple bitwise conversion, so results will differ on a platform on
which NULL is not all bits zero.


Actually the cast is implementation-defined (and the implementation
can define it to be UB in some, or all, cases).


No, it can't. The term 'implementation-defined' under the C standard
does not allow an implementation to turn implementation-defined
behavior into undefined behavior. For implementation-defined
behavior, the implement must perform consistently according to rules
which it must document.

A classic example of implementation-defined behavior is whether right
shifts of signed integer types sign-extend or truncate. The
implementation must specify which behavior it will produce under any
specific sets of circumstances, and then must consistently provide
that behavior.
It would make sense for an implementation to give 0 for
(int)(void *)0, even if (void *)0 were not all bits zero.

The expression (int)&((doomdata*)0)->b is relying on 2 non-standard
things:
- the compiler doesn't actually dereference 0
- the cast to int works as if pointers are ints in a flat
memory model and (int)NULL == 0

It's irrelevant to this expression whether NULL is all-bits-zero
or not.


Agreed, with everything else.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #34
On Tue, 30 Nov 2004 00:44:39 +0000, Christian Bau
<ch***********@cbau.freeserve.co.uk> wrote in comp.lang.c:
In article <31*************@uni-berlin.de>,
"S.Tobias" <si***@FamOuS.BedBuG.pAlS.INVALID> wrote:
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
The result of conversion from int to for example double*
is defined directly in one special case: When the int is a null pointer
constant, that is the value is 0, and it is an integer constant
expression.


No, the value is not important here, you're misquoting the Standard.
The Standard says: "integer constant expression with the value 0",
so first you look up what is an integer constant expression, and
then you filter only those that have the value zero. Examples are:
0
0x0
0u
(1-1)
'\000'
(int)0.0
7/11
sizeof 13 - sizeof (int)


So you are saying that I should look at integer constant expressions
with a value of zero, instead of looking at integers with a value of
zero that are integer constant expressions???


Yes, because an integer with a value of anything cannot be an integer
constant expression. The value of an object, even one with a const
qualifier, is not and never can be an integer constant expression in
C, although it can be down the hall for our friends in comp.lang.c++.

Here's the definition of the term "integer constant expression",
defined in paragraph 6 of 6.6 "Constant expressions" of the 1999
standard:

[begin]
An integer constant expression shall have integer type and shall
only have operands that are integer constants, enumeration constants,
character constants, sizeof expressions whose results are integer
constants, and floating constants that are the immediate operands of
casts. Cast operators in an integer constant expression shall only
convert arithmetic types to integer types, except as part of an
operand to the sizeof operator.
[end]

Notice that there is absolutely no mention of the value of an object,
whether const qualified or not.

Put it another way, an integer constant expression is the type you
need to define an array:

int x [27]; /* OK */

....but:

const int array_size = 27;
int x [array_size]; /* illegal at file scope even with C99 */
/* legal as automatic at block scope */
/* as a variable length array in C99, */
/* completely illegal prior to C99 */
But since the result of a conversion only depends on the
value converted and nothing else, the result of converting _any_ int of
value 0 must always be the same. As it is a null pointer in some cases,
it must be a null pointer in all cases.
The Std does not say such a thing.


The Standard says that the conversion is implementation-defined.
"Implementation-defined" implies "defined". "Defined" implies: The
implementation gives rules what the result will be if a value is
converted. The result of _any_ operation only depends on the values
involved (and of course on the definitions given by the C Standard or
the implementation) and nothing else; for example it is independent of
the representation of a value. Now add two and two together and you get
exactly what I said.


No you are wrong. There are several special cases for constant
expressions in source code, which are translated by the compiler at
compile time.

Consider:

char alert1 [3] = "\a";
char alert2 [3] = { '\\', 'a', '\0' };

The compiler performs not one but two compile time constant
conversions on the initialization for alter1. First it converts the
two source file characters \a into a single implementation-defined
character. Second, it converts the quoted string by stripping away
the quotes and placing a '\0' terminator at the end. The result is a
string with strlen() of 1.

The compiler performs two compile time constant conversions on alert2,
one similar to alert1 and the other completely different. The first
is to convert the source file characters \\ into a single
implementation-defined character in the execution character set.
Secondly, it converts 'a' from the source to the execution character
set, if they happen to be different. The result is a string with a
strlen() of 2.

If puts(alert1) causes your computer to emit an audible beep and a
newline, puts(alert2) will most certainly not cause a beep. Instead
it will send the two printable characters '\' and 'a' to the standard
output followed by a newline. Nary an audible sound to be heard.
If it wanted the value 0 to be
converted into a null pointer, it would say "integer expression with
the value 0" or just "integer value 0".


That assumption is naive.


Your assumption that the value of any object can be a "constant
expression" is what is naive. The term "null pointer constant" is
specifically defined in paragraph 3 of 6.2.6.3 as:

[begin]
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant.55) 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 any object or function.
[end]

Notice the explicit "integer constant expression", not "integer
value", and as in the snippet I quoted above, "integer constant
expression" does not include the value of an integer object or any
other object.
Integer constant expression
is a special case that a compiler must recognize at compile-time.


Only in order to allow an implicit conversion to an appropriate pointer
type that wouldn't be present otherwise. The conversion itself is
implementation defined, with one special case defined by the C Standard.
Conversions between pointer and integer types are explicitly
implementation defined.


As an implementation must conform to _everything_ that the C Standard
guarantees, an implementation is not absolutely free in how it can
define the result of that conversion. Whatever definition the C
implementor chooses, it must be consistent with the requirement that for
example (double *) 0 is a null pointer. If you look at the list of
situations where null pointer constants are recognised and handled
specially by the compiler, you will find that casts are not among them.


Yes, (double *)0 is a null pointer constant of type pointer to double.
But (double *)int_with_value_0 is not, because 0 in the first case is
an integer constant expression, but the value of the object
'int_with_value_0' is not.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #35
On Mon, 29 Nov 2004 03:57:23 GMT, "xarax" <xa***@email.com> wrote in
comp.lang.c:

"Jack Klein" <ja*******@spamcop.net> wrote in message
news:bn********************************@4ax.com...
On 28 Nov 2004 20:29:37 GMT, "S.Tobias"
<si***@FamOuS.BedBuG.pAlS.INVALID> wrote in comp.lang.c:
Jack Klein <ja*******@spamcop.net> wrote:
> On 28 Nov 2004 10:41:21 GMT, "S.Tobias"
> <si***@FamOuS.BedBuG.pAlS.INVALID> wrote in comp.lang.c:
> > Mohd Hanafiah Abdullah <na**@cs.indiana.edu> wrote:

> > > typedef struct {
> > > int a;
> > > int b;
> > > } doomdata;

> > Would this be correct?
> > (int)&((doomdata*)0)->a;

> Technically it is still undefined behavior, as the semantics of the
> expression dereference a null pointer.

(This deserves a separate thread, but since I asked the above
question here, I'll continue here too.)

As I understand the expression constitutes an access to the structure
member.

1. Does a member access constitute an access to the *whole* structure?
eg.:


The term 'access' is really only used in the C standard in conjunction
with the volatile qualifier, where the wording is unfortunately vague
enough that it can be construed several different ways.
struct A { int i; int _i; };
struct B { int i; float f; };
struct A a = {0};
struct B *pb = (struct B*)&a;
pb->i; //UB?
(*pb).i; //UB?


The two statements above actually have defined behavior, but not for
the reason you might think. The language guarantees that a pointer to
structure, suitably cast, is also a pointer to its first member. So
in given that 'pb' holds any of the following:

- a pointer to any structure type whose first member is an int

- a pointer to an array of ints

- a pointer to a single int

...and the int pointed to has a valid value, the expressions, though
not recommended, will work as designed. The compiler must generate
code equivalent to *(int *)pb, and if there is actually an int there
all is well.
Do I access the first int sub-object in `a' only, or do I access
the whole object `a'?


Now that depends what you mean by access. Let's assume a Pentium
(1/2/3/4) platform with a typical compiler, which means the size of
either of your structures is 8 8-bit bytes. Now let's also assume
that this implementation allocates all structures on an address evenly
divisible by 8, a not uncommon performance feature of such
implementations.

With the assumptions above, if the processor needs to access the
structure from memory it will perform a 64-bit access physically, so
even though your code does not direct the abstract machine to touch
the value of other members in any way, the entire memory space holding
the structure will be physically read.
2. I see certain similarity between structs and arrays (in fact,
both are called "aggregates").
Why is it that for array:
&a[5];
doesn't constitute object access (6.5.3.2#3), whereas for struct:
&s.m;
&ps->m;
the expressions do constitute access?
Why is the language designed like this?


There are actually more differences than similarities between structs
and arrays, despite the fact that both are aggregates. Structs are
first class objects, meaning they can be assigned, passed to and
returned from functions by value, and their names are never implicitly
converted to pointers. Arrays are not first class objects and do not
share any of the characteristics above.

As for other differences in this particular case, this is spelled out
by paragraph 3 of 6.5.3.2 of C99:

[begin quotation]
The unary & operator returns the address of its operand. If the
operand has type ''type'', the result has type ''pointer to type''. If
the operand is the result of a unary * operator, neither that operator
nor the & operator is evaluated and the result is as if both were
omitted, except that the constraints on the operators still apply and
the result is not an lvalue. Similarly, if the operand is the result
of a [] operator, neither the & operator nor the unary * that is
implied by the [] is evaluated and the result is as if the & operator
were removed and the [] operator were changed to a + operator.
Otherwise, the result is a pointer to the object or function
designated by its operand.
[end quotation]

Note the differences between applying '&' to the result of a '*'
operator and to the result of a '[]' operator. In the former case,
neither '&' nor '*' are evaluated as such, but note "the constraints
on the operators still apply".

Now let's back up to paragraph 1 of 6.5.3.2, which lists the
constraints for the unary '&' operator:

[begin quotation]
The operand of the unary & operator shall be either a function
designator, the result of a [] or unary * operator, or an lvalue that
designates an object that is not a bit-field and is not declared with
the register storage-class specifier.
[end quotation]

Notice that the expression under discussion,

(int)&((doomdata*)0)->a;

...is none of these things. Specifically, the operand of the '&'
operator, '((doomdata*)0)->a' is:

- not a function designator

- not the result of a [] operator

- not the result of a unary * operator


Yes it is unary *. a->b is an alias for (*a).b


No, it is not. it is the result of the -> operator. Nor is what you
call an 'alias' the result of a unary * operator, either. It is the
result of the . operator. On of the operands to the . operator is the
unary *, but the final expression is the result of the . operator.

Given:

int x [3] = { 1, 2, 3 };

Then:

*x
*(x + 1)

Are expressions that are the result of unary * operator, but:

*x + 1

The expression above it the result of the '+' operator, one of whose
operands happens to be the result of the unary * operator.
- and, because of the null pointer, not an lvalue

Finally consider one last thing, namely that regardless of whether
there is an actual access to an object, the expression explicitly
performs pointer arithmetic on a null pointer, and such use of a null
pointer is undefined in and of itself.


Therefore, & cancels out the -> to yield a simple
integer constant that is resolved at compile time.


No, the standard specifically states that the combinations "&[]" and
"&*" cancel out. Go back and read the quotation from paragraph 3,
above. Where does it state that the combinations "&." or "&->" cancel
out? Nowhere. So they don't.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #36
On Sun, 28 Nov 2004 20:31:10 GMT, "xarax" <xa***@email.com> wrote in
comp.lang.c:
"Jack Klein" <ja*******@spamcop.net> wrote in message
news:ov********************************@4ax.com...
On Sun, 28 Nov 2004 11:26:06 +0100, Michael Mair
<Mi**********@invalid.invalid> wrote in comp.lang.c:
Mohd Hanafiah Abdullah wrote:

> Is the following code conformat to ANSI C?
>
> typedef struct {
> int a;
> int b;
> } doomdata;
>
> int main(void)
> {
> int x;
>
> x = (int)&((doomdata*)0)->b;
> printf("x=%d\n", x);
> return x;
> }
>
> The part,
> (int)&((doomdata*)0)->b;
>
> is it conformant to ANSI C? What is it supposed to do?

According to prior discussions, it is at least shady but
probably not conformant, depending on whether the address
0 is dereferenced or not.


No it has undefined behavior, and has nothing to do with the "address
0", but with a null pointer. C is defined in terms of an abstract
machine, and in the abstract machine this expression dereferences a
null pointer.

/snip/

It does not dereference any pointer. The & cancels out
the ->.


I see now that you have repeated the statement above quite a few times
in this thread. I provided a much more detailed reply to one of your
direct responses to me farther down the thread, but I will repeat part
of it here:

The & operator does NOT cancel out the -> dereference operator, nor
does it cancel out the . operator. I quoted C99 6.5.3.2#3 from the
standard in where it states that the & operator essentially cancels
out the [] operator and the * operator. I know you have seen that
citation, you quoted it in a reply to my post that contained it.

If you want to keep contending that it cancels the . or -> comma
operators, put up or shut up. Cite a section of the C standard that
says so, as mine did for two other operators, or admit that you are
wrong.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #37
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
In article <31*************@uni-berlin.de>,
"S.Tobias" <si***@FamOuS.BedBuG.pAlS.INVALID> wrote:
Conversions between pointer and integer types are explicitly
implementation defined.


As an implementation must conform to _everything_ that the C Standard
guarantees, an implementation is not absolutely free in how it can
define the result of that conversion. Whatever definition the C
implementor chooses, it must be consistent with the requirement that for
example (double *) 0 is a null pointer. If you look at the list of
situations where null pointer constants are recognised and handled
specially by the compiler, you will find that casts are not among them.


In addition to what Jack already wrote, that last statement is plain
wrong for the simple reason that there is no such list. What the
Standard says, in 6.3.2.3#3, is

# If a null pointer constant is converted to a pointer type, the
# resulting pointer, called a null pointer, is...

Since a cast to double * definitely (in fact, quite explicitly) _is_ a
conversion to pointer type, it is the very paragraph which defines both
null pointer constants and null pointers which demands that (double *)0
results in a null pointer.
As for an integer _object_, or any other non-constant integer
expression, with the value zero, the Standard makes no such exception
for them. The only exception is for null pointer _constants_, and that
exception is both made explicit, and mentioned again in 6.3.2.3#6 _as
the only exception_.

Richard
Nov 14 '05 #38
Jack Klein <ja*******@spamcop.net> wrote:
Old Wolf wrote:

Actually the cast is implementation-defined (and the implementation
can define it to be UB in some, or all, cases).


No, it can't. The term 'implementation-defined' under the C standard
does not allow an implementation to turn implementation-defined
behavior into undefined behavior. For implementation-defined
behavior, the implement must perform consistently according to rules
which it must document.


Thanks for the clarification. I think what I was trying to
say is, the implementation could define it as a hardware
exception (or some other condition that terminates the program).
(Is that right?)
Nov 14 '05 #39
In article <41****************@news.individual.net>,
rl*@hoekstra-uitgeverij.nl (Richard Bos) wrote:
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
In article <31*************@uni-berlin.de>,
"S.Tobias" <si***@FamOuS.BedBuG.pAlS.INVALID> wrote:
Conversions between pointer and integer types are explicitly
implementation defined.
As an implementation must conform to _everything_ that the C Standard
guarantees, an implementation is not absolutely free in how it can
define the result of that conversion. Whatever definition the C
implementor chooses, it must be consistent with the requirement that for
example (double *) 0 is a null pointer. If you look at the list of
situations where null pointer constants are recognised and handled
specially by the compiler, you will find that casts are not among them.


In addition to what Jack already wrote, that last statement is plain
wrong for the simple reason that there is no such list. What the
Standard says, in 6.3.2.3#3, is

# If a null pointer constant is converted to a pointer type, the
# resulting pointer, called a null pointer, is...

Since a cast to double * definitely (in fact, quite explicitly) _is_ a
conversion to pointer type, it is the very paragraph which defines both
null pointer constants and null pointers which demands that (double *)0
results in a null pointer.


The "special handling" happens in cases like

int* p = 0; // Only legal because of special handling
if (p == 0)... // Only legal because of special handling

Both examples would be illegal if you replace 0 with 1. (double *) 1 is
perfectly legal, with implementation defined behavior.
As for an integer _object_, or any other non-constant integer
expression, with the value zero, the Standard makes no such exception
for them. The only exception is for null pointer _constants_, and that
exception is both made explicit, and mentioned again in 6.3.2.3#6 _as
the only exception_.


The Standard does not have an explicit written rule for converting
integers of value 0 that are not null pointer constants, but the
"implementation defined behavior" cannot distinguish between a zero that
is a null pointer constant and a zero that is not a null pointer
constant. The conversion is _only_ based on the value. The C Standard
gives an explicit guarantee (gives an explicit requirement for any
conforming implementation) that in a certain subset of all situations
where a value of 0 is converted, the result will be a null pointer. The
implementation has no choice but converting _all_ zero values to null
pointers.
Nov 14 '05 #40
On Wed, 01 Dec 2004 00:12:51 +0000, Christian Bau
<ch***********@cbau.freeserve.co.uk> wrote in comp.lang.c:
In article <41****************@news.individual.net>,
rl*@hoekstra-uitgeverij.nl (Richard Bos) wrote:
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
In article <31*************@uni-berlin.de>,
"S.Tobias" <si***@FamOuS.BedBuG.pAlS.INVALID> wrote:

> Conversions between pointer and integer types are explicitly
> implementation defined.

As an implementation must conform to _everything_ that the C Standard
guarantees, an implementation is not absolutely free in how it can
define the result of that conversion. Whatever definition the C
implementor chooses, it must be consistent with the requirement that for
example (double *) 0 is a null pointer. If you look at the list of
situations where null pointer constants are recognised and handled
specially by the compiler, you will find that casts are not among them.


In addition to what Jack already wrote, that last statement is plain
wrong for the simple reason that there is no such list. What the
Standard says, in 6.3.2.3#3, is

# If a null pointer constant is converted to a pointer type, the
# resulting pointer, called a null pointer, is...

Since a cast to double * definitely (in fact, quite explicitly) _is_ a
conversion to pointer type, it is the very paragraph which defines both
null pointer constants and null pointers which demands that (double *)0
results in a null pointer.


The "special handling" happens in cases like

int* p = 0; // Only legal because of special handling
if (p == 0)... // Only legal because of special handling

Both examples would be illegal if you replace 0 with 1. (double *) 1 is
perfectly legal, with implementation defined behavior.
As for an integer _object_, or any other non-constant integer
expression, with the value zero, the Standard makes no such exception
for them. The only exception is for null pointer _constants_, and that
exception is both made explicit, and mentioned again in 6.3.2.3#6 _as
the only exception_.


The Standard does not have an explicit written rule for converting
integers of value 0 that are not null pointer constants, but the
"implementation defined behavior" cannot distinguish between a zero that
is a null pointer constant and a zero that is not a null pointer
constant. The conversion is _only_ based on the value. The C Standard
gives an explicit guarantee (gives an explicit requirement for any
conforming implementation) that in a certain subset of all situations
where a value of 0 is converted, the result will be a null pointer. The
implementation has no choice but converting _all_ zero values to null
pointers.


You are still confused. If you look again at the excerpt from the
standard that defines an "integer constant expression" you will note
that it is an expression with a value known to the compiler at COMPILE
TIME.

When you convert, with a cast, the value of an integer object to a
pointer, the value of that integer object is accessed and evaluated at
RUN TIME.

So the implementation-defined behavior can most certainly distinguish
between a "zero that is a null pointer constant" and a zero that is
not a null pointer constant, whatever that means, presumably the value
of some object that happens to equal 0.

It is probably not even allowable in the RUN TIME operation of
converting an integer value to a pointer with an appropriate cast,
that the implementation could check whether the value evaluates to 0
and if so, convert it to the bit pattern of a null pointer even if its
pattern for a null pointer is not all bits 0.

This is because, assuming an implementations has an integer type large
enough to hold a pointer, then:

-- a conversion integer -> pointer -> integer must end up with the
final integer having the same value as the original, and...

-- a conversion pointer -> integer -> pointer to same type must end up
with the final pointer containing the same address as the original.

So if an implementation had a representation for null pointers that
was not all bits 0, it could either convert integers with the value 0
to that null pointer representation, or integers with the bit pattern
of the null pointer to a null pointer, but not both, otherwise when
converting a null pointer back to an integer type it could not know
whether to generate 0 or the null pointer bit pattern in the
destination integer.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #41
"Jack Klein" <ja*******@spamcop.net> wrote in message
news:hg********************************@4ax.com...
This is because, assuming an implementations has an integer type large
enough to hold a pointer, then:

-- a conversion integer -> pointer -> integer must end up with the
final integer having the same value as the original, and...
Where does C99 state this ?
-- a conversion pointer -> integer -> pointer to same type must end up
with the final pointer containing the same address as the original.
That is not the language of the standard : it says nothing about such a
conversion.
All it says is that the result of converting a valid pointer to void* or char*
and back to the original pointer type will yield a pointer that compares equal
to the original pointer. "Containing the same address" is not necessary, nor is
it a concept defined in the standard.
So if an implementation had a representation for null pointers that
was not all bits 0, it could either convert integers with the value 0
to that null pointer representation, or integers with the bit pattern
of the null pointer to a null pointer, but not both, otherwise when
converting a null pointer back to an integer type it could not know
whether to generate 0 or the null pointer bit pattern in the
destination integer.


I don't think you can say that.

--
Chqrlie.
Nov 14 '05 #42
On Tue, 30 Nov 2004 15:04:37 -0800, Old Wolf wrote:
Jack Klein <ja*******@spamcop.net> wrote:
Old Wolf wrote:
>
> Actually the cast is implementation-defined (and the implementation
> can define it to be UB in some, or all, cases).


No, it can't. The term 'implementation-defined' under the C standard
does not allow an implementation to turn implementation-defined
behavior into undefined behavior. For implementation-defined
behavior, the implement must perform consistently according to rules
which it must document.


Thanks for the clarification. I think what I was trying to
say is, the implementation could define it as a hardware
exception (or some other condition that terminates the program).
(Is that right?)


The standard says that the *result* is implementation-defined. That means
that the operation has to produce a result. However that result may be a
trap representation, so a subsequent operation that uses the result can
produce undefined behaviour.

Lawrence

Nov 14 '05 #43
Charlie Gordon wrote:

"Jack Klein" <ja*******@spamcop.net> wrote in message
news:hg********************************@4ax.com...
This is because,
assuming an implementations has an integer type large
enough to hold a pointer, then:

-- a conversion integer -> pointer -> integer must end up with the
final integer having the same value as the original, and...


Where does C99 state this ?
-- a conversion pointer -> integer -> pointer to same type
must end up
with the final pointer containing the same address as the original.


That is not the language of the standard :
it says nothing about such a conversion.
All it says is that the result of converting a valid
pointer to void* or char*
and back to the original pointer type will yield a pointer that
compares equal
to the original pointer. "Containing the same address"
is not necessary, nor is it a concept defined in the standard.
So if an implementation had a representation for null pointers that
was not all bits 0,
it could either convert integers with the value 0
to that null pointer representation,
or integers with the bit pattern
of the null pointer to a null pointer, but not both, otherwise when
converting a null pointer back to an integer type it could not know
whether to generate 0 or the null pointer bit pattern in the
destination integer.


I don't think you can say that.


I don't think the standard prohibits an implementation
from yielding the integer value of five,
for every pointer to integer conversion.

--
pete
Nov 14 '05 #44
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
In article <41****************@news.individual.net>,
rl*@hoekstra-uitgeverij.nl (Richard Bos) wrote:
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
Whatever definition the C
implementor chooses, it must be consistent with the requirement that for
example (double *) 0 is a null pointer. If you look at the list of
situations where null pointer constants are recognised and handled
specially by the compiler, you will find that casts are not among them.


In addition to what Jack already wrote, that last statement is plain
wrong for the simple reason that there is no such list. What the
Standard says, in 6.3.2.3#3, is

# If a null pointer constant is converted to a pointer type, the
# resulting pointer, called a null pointer, is...

Since a cast to double * definitely (in fact, quite explicitly) _is_ a
conversion to pointer type, it is the very paragraph which defines both
null pointer constants and null pointers which demands that (double *)0
results in a null pointer.


The "special handling" happens in cases like


I quote the Standard at you, and you deny that it says what it does say?
A null pointer constant is converted to a null pointer in _all_ cases
where it is converted to a pointer type. Which particular construct is
used to convert it is _not_ important. That is what the above quotation
of the Standard says. If you wish to deny this, present arguments.

Richard
Nov 14 '05 #45
In article <hg********************************@4ax.com>,
Jack Klein <ja*******@spamcop.net> wrote:
You are still confused. If you look again at the excerpt from the
standard that defines an "integer constant expression" you will note
that it is an expression with a value known to the compiler at COMPILE
TIME.
Nonsense. The conversion is done at RUN TIME in the abstract machine.
The C Standard gives a requirement what the result has to be before the
compiler is even written, the compiler knows at compile time what the
result has to be, the conversion will most likely not generate any code,
which is allowed by the "as if" rule, but the conversion is done _at run
time_.
When you convert, with a cast, the value of an integer object to a
pointer, the value of that integer object is accessed and evaluated at
RUN TIME.
In the case

int i = 0;
char* p = (char *) i;

the compiler knows what the result will be exactly in the same way as in
the case

char* p = 0;

The same conversion happens in both cases. The only difference: In the
second case the C Standard gives a requirement that explicitely mentions
the case, in the first case the compiler knows that exactly the same
conversion happens, so the result must be the same.

So the implementation-defined behavior can most certainly distinguish
between a "zero that is a null pointer constant" and a zero that is
not a null pointer constant, whatever that means, presumably the value
of some object that happens to equal 0.

It is probably not even allowable in the RUN TIME operation of
converting an integer value to a pointer with an appropriate cast,
that the implementation could check whether the value evaluates to 0
and if so, convert it to the bit pattern of a null pointer even if its
pattern for a null pointer is not all bits 0.
Of course it is allowed.
This is because, assuming an implementations has an integer type large
enough to hold a pointer, then:

-- a conversion integer -> pointer -> integer must end up with the
final integer having the same value as the original, and...
Right.
-- a conversion pointer -> integer -> pointer to same type must end up
with the final pointer containing the same address as the original.
Right. So if an implementation had a representation for null pointers that
was not all bits 0, it could either convert integers with the value 0
to that null pointer representation, or integers with the bit pattern
of the null pointer to a null pointer, but not both
Right.
otherwise when
converting a null pointer back to an integer type it could not know
whether to generate 0 or the null pointer bit pattern in the
destination integer.


Correct. If the representation for a null pointer is not all bits zero,
then conversions in both directions cannot leave the representation
unchanged.
Nov 14 '05 #46
In article <41****************@news.individual.net>,
rl*@hoekstra-uitgeverij.nl (Richard Bos) wrote:
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
In article <41****************@news.individual.net>,
rl*@hoekstra-uitgeverij.nl (Richard Bos) wrote:
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:

> Whatever definition the C
> implementor chooses, it must be consistent with the requirement that
> for
> example (double *) 0 is a null pointer. If you look at the list of
> situations where null pointer constants are recognised and handled
> specially by the compiler, you will find that casts are not among them.

In addition to what Jack already wrote, that last statement is plain
wrong for the simple reason that there is no such list. What the
Standard says, in 6.3.2.3#3, is

# If a null pointer constant is converted to a pointer type, the
# resulting pointer, called a null pointer, is...

Since a cast to double * definitely (in fact, quite explicitly) _is_ a
conversion to pointer type, it is the very paragraph which defines both
null pointer constants and null pointers which demands that (double *)0
results in a null pointer.
The "special handling" happens in cases like


I quote the Standard at you, and you deny that it says what it does say?


I deny that you found what was relevant to the discussion.
A null pointer constant is converted to a null pointer in _all_ cases
where it is converted to a pointer type. Which particular construct is
used to convert it is _not_ important. That is what the above quotation
of the Standard says. If you wish to deny this, present arguments.


You are confusing two things. A. The C Standard says what the result of
converting a null pointer constant is. B. The C Standard gives a list of
situations, where an integer would normally not be allowed, except when
that integer is a null pointer constant, in which case it is converted
to an appropriate pointer.
Nov 14 '05 #47
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
In article <41****************@news.individual.net>,
rl*@hoekstra-uitgeverij.nl (Richard Bos) wrote:
A null pointer constant is converted to a null pointer in _all_ cases
where it is converted to a pointer type. Which particular construct is
used to convert it is _not_ important. That is what the above quotation
of the Standard says. If you wish to deny this, present arguments.


You are confusing two things. A. The C Standard says what the result of
converting a null pointer constant is. B. The C Standard gives a list of
situations, where an integer would normally not be allowed, except when
that integer is a null pointer constant, in which case it is converted
to an appropriate pointer.


Right. C&V, or get lost.

Richard
Nov 14 '05 #48
Christian Bau <ch***********@cbau.freeserve.co.uk> wrote:
In the case int i = 0;
char* p = (char *) i; the compiler knows what the result will be exactly in the same way as in
the case char* p = 0; The same conversion happens in both cases.

^^^^^^^^^^^^^^^^^^^
Prove it!

In 6.3.2.3 the Standard describes several types of conversions involving
pointer types. Just prove that conversion from null pointer constant
to a pointer type and conversion from integer type to pointer type
are the *same kind of conversions*, and I'm with you.

(Whether it happens and compile- or run-time, doesn't matter.)

--
Stan Tobias
mailx `echo si***@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 14 '05 #49
Jack Klein <ja*******@spamcop.net> wrote:
On 28 Nov 2004 20:29:37 GMT, "S.Tobias"
<si***@FamOuS.BedBuG.pAlS.INVALID> wrote in comp.lang.c:
As I understand the expression constitutes an access to the structure
member.

1. Does a member access constitute an access to the *whole* structure?
eg.: The term 'access' is really only used in the C standard in conjunction
with the volatile qualifier, where the wording is unfortunately vague
enough that it can be construed several different ways.
I was thinking "access for value", referring to clauses such as
6.2.6 ("Representations ...") and 6.5#7 (aliasing rules).
struct A { int i; int _i; };
struct B { int i; float f; };
struct A a = {0};
struct B *pb = (struct B*)&a;
pb->i; //UB?
(*pb).i; //UB? The two statements above actually have defined behavior, but not for
the reason you might think. The language guarantees that a pointer to
structure, suitably cast, is also a pointer to its first member. So
in given that 'pb' holds any of the following: - a pointer to any structure type whose first member is an int - a pointer to an array of ints - a pointer to a single int ...and the int pointed to has a valid value, the expressions, though
not recommended, will work as designed. The compiler must generate
code equivalent to *(int *)pb, and if there is actually an int there
all is well.
Just looking at the last expression makes me feel dizzy, this is because
*pb;
is definitely UB. Accessing `a' by use of unrelated type (struct B) lvalue
in the original expression doesn't look good, but indeed I haven't found
anything that would render it invalid.

Do I access the first int sub-object in `a' only, or do I access
the whole object `a'?
(By accessing the whole struct I understood operation semantically
equivalent to:
*pb;
)

After careful reading of 6.3.2.1 ("Lvalues, arrays, ...") and 6.5.2.3
("Structure and union members") I came to the following conclusions.
Could you please check my reasoning.

I understand that "access for value" is when lvalue is converted
to value, as described in 6.3.2.1#2.

(*pb).i;

*pb is an lvalue (6.5.3.2#4). Since this lvalue is a left operand of `.'
operator, it is not converted to value (6.3.2.1#2), hence it is not an
object access for value. (*pb).i is an lvalue and in above expression
is converted to value.
&pb->i;

Similarly as above, pb->i is an lvalue, and since it is an operand of
the `&' operator, it is not converted to a value (6.3.2.1#2), so
the value of object pb->i is not accessed. Neither the whole struct
nor its member are accessed for value.

2. I see certain similarity between structs and arrays (in fact,
both are called "aggregates").
Why is it that for array:
&a[5];
doesn't constitute object access (6.5.3.2#3), whereas for struct:
&s.m;
&ps->m;
the expressions do constitute access?
Why is the language designed like this?


As I have shown above, I thought wrong before. The member of the struct
is not accessed, because in either case the lvalues are operands to `&'
operator and are not converted to values.

There are actually more differences than similarities between structs [snip]

I completely agree with what you said. Sometimes I imagine
a struct as sort of an "array" which holds different objects,
and expression
s.member3; //starts from `member0'
is sort of conceptually corresponding to
a[3];
This is a very vague similarity and is a matter of personal POV.

Now let's back up to paragraph 1 of 6.5.3.2, which lists the
constraints for the unary '&' operator: [begin quotation]
The operand of the unary & operator shall be either a function
designator, the result of a [] or unary * operator, or an lvalue that
designates an object that is not a bit-field and is not declared with
the register storage-class specifier.
[end quotation] Notice that the expression under discussion, (int)&((doomdata*)0)->a; ...is none of these things. Specifically, the operand of the '&'
operator, '((doomdata*)0)->a' is: - not a function designator - not the result of a [] operator - not the result of a unary * operator - and, because of the null pointer, not an lvalue
I mildly disagree at the last point. 6.5.2.3#4 says that the result
of `->' operator is an lvalue. But this is not exactly what I want
to say.

The quotation you brought comes from Constraints. Lvalue is an expression
which *potentially* designates an object.
int *pi;
/*...*/
++(*pi);
In the last expression `pi' might point to an object or not; the compiler
cannot diagnose this. Nevertheless *pi is an lvalue expression.

For same reasons in:
++(*(int*)NULL);
I believe that *(int*)NULL is an lvalue expression (it is not a constraint
violation), though the compiler might indeed diagnose this early.

I think this example illustrates my point best:
int *pi = malloc(sizeof *pi);
*pi is an lvalue, although at this stage it doesn't point to any object.

Finally consider one last thing, namely that regardless of whether
there is an actual access to an object, the expression explicitly
performs pointer arithmetic on a null pointer, and such use of a null
pointer is undefined in and of itself.


I can believe it. The compiler internally must do something like
(int) ((char*)NULL + offset(doomdata, a))
But is it formally stated so? I can't think of any place where the
Standard describes member access as related to any pointer arithmetic.

--
Stan Tobias
mailx `echo si***@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 14 '05 #50

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: James Harris | last post by:
Having updated my Debian system it now complains that I am using an incompatible pointer type in warnings such as "passing arg 2 of 'bind' from incompatible pointer type" from code, struct...
5
by: jimjim | last post by:
Hello, Any help will be much appreciatted. My problem is as follows: I declare as global variables: typedef struct _Node{ ..; ..;}Node; Node *Graph; in a function called initiallise(), I...
2
by: Peter Dunker | last post by:
Hi, I will write ANSI C89. Is the following struct defenition correct ? I wrote it with VC6(Windows IDE) and at first no Problem. As I changed a compiler switch to 'no language extension', the...
5
by: PCHOME | last post by:
Hello! I am working on dividing a single C file into several files. Now I encounter a problem about the global variables and can not find a way to solve it. All global variables and codes used...
4
by: lp-boy | last post by:
Hello! Is the following code legal? template<class T> struct holder{}; struct A { holder<struct B*> h; //!!!
9
by: werasm | last post by:
Hi all, What is the difference between: typedef struct { ... } MyS1; ....and...
28
by: Bill | last post by:
Hello All, I am trying to pass a struct to a function. How would that best be accomplished? Thanks, Bill
2
by: beet | last post by:
Hi all, I tried to declare a c++ struct like following in a header file; I want to include this header file in other files to create and access this struct. ------ 1 #ifndef _SEARCHDATA_H_...
6
by: BIll Cunningham | last post by:
pg 128 of kandr2 speaks of a struct struct point{ int x; int y; }; I understand this but what is it saying about using intialize variables like such.
4
by: Lew Pitcher | last post by:
(having trouble getting my reply through - hopefully, third time is a charm) On November 11, 2008 19:53, in comp.lang.c, BIll Cunningham (nospam@nspam.invalid) wrote: Why an int? Because K&R...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
0
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated ...
0
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.