address of static array element as address constant

J

John Koleszar

Hi all,

I'm porting some code that provides compile-time assertions from one
compiler to another and ran across what I believe to be compliant code
that won't compile using the new compiler. Not naming names here to
remove bias - I'm trying to tell if I'm relying on implementation defined
behavior or if this is a bug in the new compiler.

Consider this stripped down example:

#include <stddef.h>
struct s { int a[3]; int b; };
void foo(void) {
switch(0) {case offsetof(struct s,a[0]):;}
}

The relevant part of the C standard says that:
7.17 Common definitions <stddef.h>
<snip>
3 The macros are
<snip>
offsetof(type, member-designator)
which expands to an integer constant expression that has type size_t,
the value of which is the offset in bytes, to the structure member
(designated by member-designator), from the beginning of its structure
(designated by type). The type and member designator shall be such that
given
static type t;
then the expression &(t.member-designator) evaluates to an address
constant. (If the specified member is a bit-field, the behavior is
undefined.)

It's my feeling that &(t.a[0]) does (can, should) evaluate to an address
constant, as the types are all complete. Other compilers (at least two
different vendors) are able to evaluate this expression as an address
constant. However, the new compiler raises an error 'case expression not
constant.' The new compiler is able to properly evaluate the offsetof a
or b, but not any element of a. What I'd really like is offsetof c.bar
[0].baz, but it seems to choke on any subscripted element. a[0] does work
as part of a non-constant expression though, eg printf(offsetof(a[0]))

So, what say the language lawyers?

br,

John
 
P

Peter Nilsson

John said:
...Consider this stripped down example:

#include <stddef.h>
struct s { int a[3]; int b; };
void foo(void) {
switch(0) {case offsetof(struct s,a[0]):;}
}

The relevant part of the C standard says that:
7.17 Common definitions <stddef.h>
<snip>
3 The macros are
<snip>
offsetof(type, member-designator)
which expands to an integer constant expression that has
type size_t, the value of which is the offset in bytes, to the
structure member (designated by member-designator), ...

Note that you have not supplied a structure member. What
you need is: offsetof(struct s, a)
from the beginning of its structure (designated by type).
The type and member designator shall be such that given
static type t;
then the expression &(t.member-designator) evaluates to
an address constant. (If the specified member is a bit-
field, the behavior is undefined.)

It's my feeling that &(t.a[0]) does (can, should) evaluate to
an address constant, as the types are all complete.

Feel away. Just realise that your feelings are incidental. :)
Other compilers (at least two different vendors) are able to
evaluate this expression as an address constant. However,
the new compiler raises an error 'case expression not
constant.' The new compiler is able to properly evaluate
the offsetof a or b, but not any element of a.

AFAICS, no implementation is required to.
What I'd really like is offsetof c.bar[0].baz, but it seems to
choke on any subscripted element.

offsetof(c, bar) + offsetof(<bar's type>, baz)
 
P

Peter Nilsson

Peter said:
John said:
What I'd really like is offsetof c.bar[0].baz, but it seems to
choke on any subscripted element.

offsetof(c, bar) + offsetof(<bar's type>, baz)

Er...

offsetof(<c's type>, bar) + offsetof(<bar's type>, baz)
 
K

Kaz Kylheku

Consider this stripped down example:

#include <stddef.h>
struct s { int a[3]; int b; };
void foo(void) {
switch(0) {case offsetof(struct s,a[0]):;}
}

Have you tried offsetof(struct s, a)?
The relevant part of the C standard says that:
7.17 Common definitions <stddef.h>
<snip>
3 The macros are
<snip>
offsetof(type, member-designator)

But a[0] is not a member designator. It is the 'a' which is the member
designator.
which expands to an integer constant expression that has type size_t,
the value of which is the offset in bytes, to the structure member
(designated by member-designator), from the beginning of its structure
(designated by type). The type and member designator shall be such that
given
static type t;
then the expression &(t.member-designator) evaluates to an address

Given t.a[0], the syntax is actually (t.a)[0]. The postfix operator []
has nothing to do with the structure member access; it's operating
on a pointer which comes out of the t.a expression.
constant. However, the new compiler raises an error 'case expression not
constant.'

Indeed, because the expression contains evaluated pointer dereferencing.

The other compiler must be accepting this as an extension.
 
J

John Koleszar

John said:
...Consider this stripped down example:

#include <stddef.h>
struct s { int a[3]; int b; };
void foo(void) {
switch(0) {case offsetof(struct s,a[0]):;}
}

The relevant part of the C standard says that:
7.17 Common definitions <stddef.h>
<snip>
3 The macros are
<snip>
offsetof(type, member-designator)
which expands to an integer constant expression that has type size_t,
the value of which is the offset in bytes, to the structure member
(designated by member-designator), ...

Note that you have not supplied a structure member. What you need is:
offsetof(struct s, a)

I agree, for the simple definition of structure member. I did consider
this clause. I suppose I focused more on the address constant portion of
the definition because I couldn't think of an example where, given that
member-designator is a simple Member, the controlling expression &
(t.member-designator) does not evaluate to an address constant. I assume
that the spec is not being redundant, so I read the term "member" a
little looser here. Maybe I'm missing something obvious. Can you show me
a case that meets the first clause (simple member) but not the second
(address constant)?
from the beginning of its structure (designated by type). The type
and member designator shall be such that given
static type t;
then the expression &(t.member-designator) evaluates to an address
constant. (If the specified member is a bit- field, the behavior is
undefined.)

It's my feeling that &(t.a[0]) does (can, should) evaluate to an
address constant, as the types are all complete.

Feel away. Just realise that your feelings are incidental. :)

Sorry, bad choice of words. s/feeling/interpretation/. I'm quite aware
that it doesn't make a lick of difference what I think :)
AFAICS, no implementation is required to.

Do you mean within the context of offsetof? I still think that &(t.a[0])
is required by the standard to be a valid address constant. From 6.6.9:

An address constant is a null pointer, a pointer to an lvalue
designating an object of static storage duration, or a pointer to a
function designator; it shall be created explicitly using the unary &
operator or an integer constant cast to pointer type, or implicitly by
the use of an expression of array or function type. The array-subscript
[] and member-access . and -> operators, the address & and indirection
* unary operators, and pointer casts may be used in the creation of an
address constant, but the value of an object shall not be accessed by
use of these operators.

FWIW, the new compiler does accept the member-access operator in the
member-designator operand, just not the array-subscript. Not relevant if
member-designator is required to be a simple member, of course.
What I'd really like is offsetof c.bar[0].baz, but it seems to choke on
any subscripted element.

offsetof(c, bar) + offsetof(<bar's type>, baz)

Of course, I know the workarounds (or the Right Way, depending on your
perspective). Just hoping to keep the syntax more compact, as expanding
it obscures the intent, thus requiring a comment and making it even more
verbose, not to mention slightly less safe (could silently fail if
typeof<s.c> != struct c):

ct_assert(offsetof(struct s, c.bar[0].baz)%4==0)
vs
ct_assert((offsetof(struct s, c)
+ offsetof(struct c, bar)
+ offsetof(struct b, baz))%4==0)

Gets worse with real names, of course. It's irrelevant as I have to work
around the issue anyway, just wanted to give a bigger picture as to what/
why I'm trying to achieve.

John
 
J

John Koleszar

John said:
...Consider this stripped down example:

#include <stddef.h>
struct s { int a[3]; int b; };
void foo(void) {
switch(0) {case offsetof(struct s,a[0]):;}
}

The relevant part of the C standard says that:
7.17 Common definitions <stddef.h>
<snip>
3 The macros are
<snip>
offsetof(type, member-designator)
which expands to an integer constant expression that has type size_t,
the value of which is the offset in bytes, to the structure member
(designated by member-designator), ...

Note that you have not supplied a structure member. What you need is:
offsetof(struct s, a)

I agree, for the simple definition of structure member. I did consider
this clause. I suppose I focused more on the address constant portion of
the definition because I couldn't think of an example where, given that
member-designator is a simple Member, the controlling expression &
(t.member-designator) does not evaluate to an address constant. I assume
that the spec is not being redundant, so I read the term "member" a
little looser here. Maybe I'm missing something obvious. Can you show me
a case that meets the first clause (simple member) but not the second
(address constant)?
from the beginning of its structure (designated by type). The type
and member designator shall be such that given
static type t;
then the expression &(t.member-designator) evaluates to an address
constant. (If the specified member is a bit- field, the behavior is
undefined.)

It's my feeling that &(t.a[0]) does (can, should) evaluate to an
address constant, as the types are all complete.

Feel away. Just realise that your feelings are incidental. :)

Sorry, bad choice of words. s/feeling/interpretation/. I'm quite aware
that it doesn't make a lick of difference what I think :)
AFAICS, no implementation is required to.

Do you mean within the context of offsetof? I still think that &(t.a[0])
is required by the standard to be a valid address constant. From 6.6.9:

An address constant is a null pointer, a pointer to an lvalue
designating an object of static storage duration, or a pointer to a
function designator; it shall be created explicitly using the unary &
operator or an integer constant cast to pointer type, or implicitly by
the use of an expression of array or function type. The array-subscript
[] and member-access . and -> operators, the address & and indirection
* unary operators, and pointer casts may be used in the creation of an
address constant, but the value of an object shall not be accessed by
use of these operators.

FWIW, the new compiler does accept the member-access operator in the
member-designator operand, just not the array-subscript. Not relevant if
member-designator is required to be a simple member, of course.
What I'd really like is offsetof c.bar[0].baz, but it seems to choke on
any subscripted element.

offsetof(c, bar) + offsetof(<bar's type>, baz)

Of course, I know the workarounds (or the Right Way, depending on your
perspective). Just hoping to keep the syntax more compact, as expanding
it obscures the intent, thus requiring a comment and making it even more
verbose, not to mention slightly less safe (could silently fail if
typeof<s.c> != struct c):

ct_assert(offsetof(struct s, c.bar[0].baz)%4==0)
vs
ct_assert((offsetof(struct s, c)
+ offsetof(struct c, bar)
+ offsetof(struct b, baz))%4==0)

Gets worse with real names, of course. It's irrelevant as I have to work
around the issue anyway, just wanted to give a bigger picture as to what/
why I'm trying to achieve.

ping... can anyone else weigh in on this, or point me to a more formal
definition of member? I didn't see it called out on the glossary with
the other formal terms. This issue is is still ambiguous in my mind.

Thanks!

-- John
 
B

Ben Bacarisse

John Koleszar said:
Of course, I know the workarounds (or the Right Way, depending on your
perspective). Just hoping to keep the syntax more compact, as expanding
it obscures the intent, thus requiring a comment and making it even more
verbose, not to mention slightly less safe (could silently fail if
typeof<s.c> != struct c):

ct_assert(offsetof(struct s, c.bar[0].baz)%4==0)
vs
ct_assert((offsetof(struct s, c)
+ offsetof(struct c, bar)
+ offsetof(struct b, baz))%4==0)

Gets worse with real names, of course. It's irrelevant as I have to work
around the issue anyway, just wanted to give a bigger picture as to what/
why I'm trying to achieve.

ping... can anyone else weigh in on this, or point me to a more formal
definition of member? I didn't see it called out on the glossary with
the other formal terms. This issue is is still ambiguous in my mind.

The advice seems sound and includes a quote from the standard. What
you seem to want (offsetof(struct s, c.bar[0].baz)) is clearly not
permitted (though it might be provided as an extension, of course) and
you have identified the correct way to do it (your second ct_assert
example). Messy, but I can't see how you can avoid it.
 
J

John Koleszar

John Koleszar said:
Of course, I know the workarounds (or the Right Way, depending on your
perspective). Just hoping to keep the syntax more compact, as expanding
it obscures the intent, thus requiring a comment and making it even more
verbose, not to mention slightly less safe (could silently fail if
typeof<s.c> != struct c):

ct_assert(offsetof(struct s, c.bar[0].baz)%4==0)
vs
ct_assert((offsetof(struct s, c)
+ offsetof(struct c, bar)
+ offsetof(struct b, baz))%4==0)

Gets worse with real names, of course. It's irrelevant as I have to work
around the issue anyway, just wanted to give a bigger picture as to what/
why I'm trying to achieve.

ping... can anyone else weigh in on this, or point me to a more formal
definition of member? I didn't see it called out on the glossary with
the other formal terms. This issue is is still ambiguous in my mind.

The advice seems sound and includes a quote from the standard. What
you seem to want (offsetof(struct s, c.bar[0].baz)) is clearly not
permitted (though it might be provided as an extension, of course) and
you have identified the correct way to do it (your second ct_assert
example). Messy, but I can't see how you can avoid it.

All of the quotes of the standard in this thread are mine, I'm just
having a bit of trouble parsing them.

My question wrt section 7.17.3 is whether the spec is being redundant or
whether there are two independant clauses in effect. That is, is there a
case where for a simple member the expression &(t.member) does not
evaluate to an address constant?

Aha, now I see it. I've been glossing over the parenthetical '(If the
specified member is a bit- field, the behavior is undefined)' which
explains the need for the second clause.

Sorry for the noise. (Now how do I send an enhancement request to
ISO :))

br,

John
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top