address of static array element as address constant

Discussion in 'C Programming' started by John Koleszar, Jul 7, 2008.

  1. 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
     
    John Koleszar, Jul 7, 2008
    #1
    1. Advertising

  2. John Koleszar wrote:
    > ...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)

    --
    Peter
     
    Peter Nilsson, Jul 7, 2008
    #2
    1. Advertising

  3. Peter Nilsson wrote:
    > John Koleszar wrote:
    > > 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)

    --
    Peter
     
    Peter Nilsson, Jul 7, 2008
    #3
  4. John Koleszar

    Kaz Kylheku Guest

    On 2008-07-07, John Koleszar <> wrote:
    >
    > 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.
     
    Kaz Kylheku, Jul 7, 2008
    #4
  5. On Sun, 06 Jul 2008 20:08:35 -0700, Peter Nilsson wrote:

    > John Koleszar wrote:
    >> ...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 :)

    >> 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.
    >


    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
     
    John Koleszar, Jul 7, 2008
    #5
  6. On Mon, 2008-07-07 at 12:11 +0000, John Koleszar wrote:
    > On Sun, 06 Jul 2008 20:08:35 -0700, Peter Nilsson wrote:
    >
    > > John Koleszar wrote:
    > >> ...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 :)
    >
    > >> 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.
    > >

    >
    > 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
     
    John Koleszar, Jul 15, 2008
    #6
  7. John Koleszar <> writes:

    > On Mon, 2008-07-07 at 12:11 +0000, John Koleszar wrote:

    <snip>
    >> 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.

    --
    Ben.
     
    Ben Bacarisse, Jul 15, 2008
    #7
  8. On Tue, 2008-07-15 at 16:07 +0100, Ben Bacarisse wrote:
    > John Koleszar <> writes:
    >
    > > On Mon, 2008-07-07 at 12:11 +0000, John Koleszar wrote:

    > <snip>
    > >> 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
     
    John Koleszar, Jul 15, 2008
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    1
    Views:
    580
    Chris Uppal
    Nov 17, 2005
  2. Nolan Martin
    Replies:
    7
    Views:
    545
    Karl Heinz Buchegger
    Jul 19, 2004
  3. Replies:
    13
    Views:
    13,004
    Kai-Uwe Bux
    Jan 22, 2007
  4. candide
    Replies:
    65
    Views:
    1,442
  5. Fredxx
    Replies:
    3
    Views:
    655
    Martin Thompson
    Jul 15, 2009
Loading...

Share This Page