[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

offsetof() Macro Attempt

Shao Miller

6/3/2011 5:57:00 PM

Good day, folks. I'm pondering 'offsetof()'. 6.6 "Constant
expressions" might be relevant.

Suppose we've:

int helper[2];

enum foo {
bar = helper + 1 - helper
};

int main(void) {
return 0;
}

Does that code include any undefined behaviour?

How about in:

int (* helper)[2];

enum foo {
bar = *helper + 1 - *helper
};

int main(void) {
return 0;
}

Is there undefined behaviour there? Note that '*helper' is an array
which "decays" to a pointer to the first element. Also note that
'helper' is a pointer to 'int[2]'. But does it matter what the stored
value of 'helper' is or is the value of 'bar' independent of it?

So let's please go on to:

char ** helper;

enum foo {
bar = *helper + 1 - *helper
};

int main(void) {
return 0;
}

Is there undefined behaviour there? So what about in:

char ** helper;

struct s {
int arr[2];
};

enum foo {
bar = ((struct s *)*helper)->arr + 1 - ((struct s *)*helper)->arr
};

int main(void) {
return 0;
}

? And then:

char ** helper;

struct s {
int x;
short y;
int z;
};

enum foo {
bar = (char *)&((struct s *)*helper)->z -
(char *)((struct s *)*helper)
};

int main(void) {
return 0;
}

In the example just above, does the value of 'bar' depend on the value
of 'helper'? Is 'helper' evaluated? If so, what if we use that mess as
part of a type-name in an operand to 'sizeof', as in:

char ** helper;

struct s {
int x;
short y;
int z;
};

enum foo {
bar = sizeof (char[
(char *)&((struct s *)*helper)->z -
(char *)((struct s *)*helper)
])
};

int main(void) {
return 0;
}

Could 'offsetof' be reasonably portable as:

char ** helper;

#define DUMMY_OF(type) ((type *)*helper)
#define ADDRESS_AT(address) ((char *)(address))
#define DUMMY_MEMBER_ADDRESS(type, member) (&DUMMY_OF(type)->member)
#define PROTECT(expression) (sizeof (char[(expression)]))
#define OFFSETOF(type, member) (PROTECT( \
ADDRESS_AT(DUMMY_MEMBER_ADDRESS(type, member)) - \
ADDRESS_AT(DUMMY_OF(type)) \
))

struct s {
int x;
short y;
int z;
};

enum foo {
bar = OFFSETOF(struct s, z)
};

int main(void) {
return 0;
}

Are there any null pointers or other dragons in there?

Thanks for any insight you can provide!
51 Answers

Carl

6/3/2011 6:00:00 PM

0

> ...
> #define DUMMY_OF(type) ((type *)*helper)
> #define ADDRESS_AT(address) ((char *)(address))
> #define DUMMY_MEMBER_ADDRESS(type, member) (&DUMMY_OF(type)->member)
> #define PROTECT(expression) (sizeof (char[(expression)]))
> #define OFFSETOF(type, member) (PROTECT( \
> ADDRESS_AT(DUMMY_MEMBER_ADDRESS(type, member)) - \
> ADDRESS_AT(DUMMY_OF(type)) \
> ))
>
> struct s {
> int x;
> short y;
> int z;
> };
>
> enum foo {
> bar = OFFSETOF(struct s, z)
> };
>
> int main(void) {
> return 0;
> }
>
> Are there any null pointers or other dragons in there?
>
> Thanks for any insight you can provide!


As far as I can tell, it deterministically returns 0 every time.

--
Bill

Shao Miller

6/3/2011 6:33:00 PM

0

On 6/3/2011 13:59, Billy Mays wrote:
>
> As far as I can tell, it deterministically returns 0 every time.

LOL. Thanks. In your response, the code has lost its indentation. Do
you see indentation in the original code sections?

pete

6/3/2011 6:49:00 PM

0

Shao Miller wrote:

> So let's please go on to:
>
> char ** helper;
>
> enum foo {
> bar = *helper + 1 - *helper
> };
>
> int main(void) {
> return 0;
> }

That one has the same problem as

int main(void)
{
int x;

x - x;
return 0;
}


--
pete

Harald van D?k

6/3/2011 8:40:00 PM

0

On Jun 3, 7:57 pm, Shao Miller <sha0.mil...@gmail.com> wrote:
> Good day, folks.  I'm pondering 'offsetof()'.  6.6 "Constant
> expressions" might be relevant.
>
> Suppose we've:
>
>    int helper[2];
>
>    enum foo {
>        bar = helper + 1 - helper
>      };
>
>    int main(void) {
>        return 0;
>      }
>
> Does that code include any undefined behaviour?

Yes.

6.6p6:
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.

Address constants are not permitted in integer constant expressions.
This is not a constraint, so a compiler is free to accept it without
any notice, but the behaviour is undefined.

Andrey Tarasevich

6/4/2011 12:16:00 AM

0

On 6/3/2011 10:57 AM, Shao Miller wrote:
> Good day, folks. I'm pondering 'offsetof()'. 6.6 "Constant
> expressions" might be relevant.
>
> Suppose we've:
>
> int helper[2];
>
> enum foo {
> bar = helper + 1 - helper
> };
>
> int main(void) {
> return 0;
> }
>
> Does that code include any undefined behaviour?

Undefined behaviour? This code is non-compilable, since enum value
specifiers are required to be integer constant expressions (ICE) and
your expression is not an ICE. ICE is not allowed to contain any address
arithmetic, even if the addresses are constant.

If your compiler somehow lets it through, it must be an extension, so
from the standard language point of view the behavior is

> How about in:
>
> int (* helper)[2];
>
> enum foo {
> bar = *helper + 1 - *helper
> };
>
> int main(void) {
> return 0;
> }

Also non-compilable. This is even more hopeless, since the address
values are not even constants.

> So let's please go on to:
>
> char ** helper;
>
> enum foo {
> bar = *helper + 1 - *helper
> };
>
> int main(void) {
> return 0;
> }

Also non-compilable for the very same reasons. The same applies to all
remaining examples.

> char ** helper;
>
> struct s {
> int x;
> short y;
> int z;
> };
>
> enum foo {
> bar = sizeof (char[
> (char *)&((struct s *)*helper)->z -
> (char *)((struct s *)*helper)
> ])
> };
>
> int main(void) {
> return 0;
> }

No hope here either. Array size in this context is also required to be
an ICE.

--
Best regards,
Andrey Tarasevich

Eric Sosman

6/4/2011 1:44:00 AM

0

On 6/3/2011 1:57 PM, Shao Miller wrote:
> Good day, folks. I'm pondering 'offsetof()'. 6.6 "Constant expressions"
> might be relevant.
>
> Suppose we've:
>
> int helper[2];
>
> enum foo {
> bar = helper + 1 - helper
> };
>
> int main(void) {
> return 0;
> }
>
> Does that code include any undefined behaviour?

No. Or, rather, "maybe." After emitting the required diagnostic
(for violation of the "shall" in 6.7.2.2p2), the compiler may reject
the program or may accept it and give it an implementation-defined
meaning. That implementation-defined meaning may leave some things
undefined, and if you run into them you'll have undefined behavior.

But you'll have the diagnostic first.

> How about in:
>
> int (* helper)[2];
>
> enum foo {
> bar = *helper + 1 - *helper
> };
>
> int main(void) {
> return 0;
> }
>
> Is there undefined behaviour there? [...]

Ditto.

> So let's please go on to:
>
> char ** helper;
>
> enum foo {
> bar = *helper + 1 - *helper
> };
>
> int main(void) {
> return 0;
> }
>
> Is there undefined behaviour there? [...]

Ditto.

> char ** helper;
>
> struct s {
> int arr[2];
> };
>
> enum foo {
> bar = ((struct s *)*helper)->arr + 1 - ((struct s *)*helper)->arr
> };
>
> int main(void) {
> return 0;
> }

Ditto.

> char ** helper;
>
> struct s {
> int x;
> short y;
> int z;
> };
>
> enum foo {
> bar = (char *)&((struct s *)*helper)->z -
> (char *)((struct s *)*helper)
> };
>
> int main(void) {
> return 0;
> }

Ditto.

> In the example just above, does the value of 'bar' depend on the value
> of 'helper'? Is 'helper' evaluated? If so, what if we use that mess as
> part of a type-name in an operand to 'sizeof', as in:
>
> char ** helper;
>
> struct s {
> int x;
> short y;
> int z;
> };
>
> enum foo {
> bar = sizeof (char[
> (char *)&((struct s *)*helper)->z -
> (char *)((struct s *)*helper)
> ])
> };
>
> int main(void) {
> return 0;
> }

Ditto.

> Could 'offsetof' be reasonably portable as:
>
> char ** helper;
>
> #define DUMMY_OF(type) ((type *)*helper)
> #define ADDRESS_AT(address) ((char *)(address))
> #define DUMMY_MEMBER_ADDRESS(type, member) (&DUMMY_OF(type)->member)
> #define PROTECT(expression) (sizeof (char[(expression)]))
> #define OFFSETOF(type, member) (PROTECT( \
> ADDRESS_AT(DUMMY_MEMBER_ADDRESS(type, member)) - \
> ADDRESS_AT(DUMMY_OF(type)) \
> ))
>
> struct s {
> int x;
> short y;
> int z;
> };
>
> enum foo {
> bar = OFFSETOF(struct s, z)
> };
>
> int main(void) {
> return 0;
> }

No.

> Are there any null pointers or other dragons in there?

No dragons, just diagnostics.

--
Eric Sosman
esosman@ieee-dot-org.invalid

Eric Sosman

6/4/2011 1:55:00 AM

0

On 6/3/2011 9:43 PM, Eric Sosman wrote:
> On 6/3/2011 1:57 PM, Shao Miller wrote:
>> Good day, folks. I'm pondering 'offsetof()'. 6.6 "Constant expressions"
>> might be relevant.
>>
>> Suppose we've:
>>
>> int helper[2];
>>
>> enum foo {
>> bar = helper + 1 - helper
>> };
>>
>> int main(void) {
>> return 0;
>> }
>>
>> Does that code include any undefined behaviour?
>
> No. Or, rather, "maybe." After emitting the required diagnostic
> (for violation of the "shall" in 6.7.2.2p2), [...]

Now having read Harald van Dijk's response, I'm less sure of
this. Failing to provide an integer constant expression violates
the constraint in 6.7.2.2p2, but the relevant part of the I.C.E.
definition is not itself a constraint. So there's a violation of
a constraint, but it rests on a violation of a non-constraint --
and the situation is unclear. So maybe I was fortunate to have
written "maybe." ;-)

--
Eric Sosman
esosman@ieee-dot-org.invalid

Tim Rentsch

6/4/2011 2:47:00 AM

0

Shao Miller <sha0.miller@gmail.com> writes:

> Good day, folks. I'm pondering 'offsetof()'. 6.6 "Constant
> expressions" might be relevant.
>
> Suppose we've:
>
> int helper[2];
>
> enum foo {
> bar = helper + 1 - helper
> };
>
> int main(void) {
> return 0;
> }
>
> Does that code include any undefined behaviour?

Because of the address constants in the defining
expression for 'bar', this code is conforming
but it is not strictly conforming.

Tim Rentsch

6/4/2011 2:59:00 AM

0

Harald van Dijk <truedfx@gmail.com> writes:

> On Jun 3, 7:57 pm, Shao Miller <sha0.mil...@gmail.com> wrote:
>> Good day, folks. I'm pondering 'offsetof()'. 6.6 "Constant
>> expressions" might be relevant.
>>
>> Suppose we've:
>>
>> int helper[2];
>>
>> enum foo {
>> bar = helper + 1 - helper
>> };
>>
>> int main(void) {
>> return 0;
>> }
>>
>> Does that code include any undefined behaviour?
>
> Yes.
>
> 6.6p6:
> 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.
>
> Address constants are not permitted in integer constant expressions.

Not exactly. Implementations are allowed to accept address
constants in integer constant expressions under 6.6p10. In
such an implementation the behavior is defined, not undefined.
The code is conforming, just not strictly conforming.

> This is not a constraint, so a compiler is free to accept it without
> any notice, but the behaviour is undefined.

If the implementation does not accept address constants in
integer constant expressions, then the definition for 'bar'
fails to be an integer constant expression, which is a
constraint violation and a diagnostic must be issued.

Tim Rentsch

6/4/2011 3:21:00 AM

0

Andrey Tarasevich <andreytarasevich@hotmail.com> writes:

> On 6/3/2011 10:57 AM, Shao Miller wrote:
>> Good day, folks. I'm pondering 'offsetof()'. 6.6 "Constant
>> expressions" might be relevant.
>>
>> Suppose we've:
>>
>> int helper[2];
>>
>> enum foo {
>> bar = helper + 1 - helper
>> };
>>
>> int main(void) {
>> return 0;
>> }
>>
>> Does that code include any undefined behaviour?
>
> Undefined behaviour? This code is non-compilable, since enum value
> specifiers are required to be integer constant expressions (ICE) and
> your expression is not an ICE. ICE is not allowed to contain any
> address arithmetic, even if the addresses are constant.

Actually they can under 6.6p10.

> If your compiler somehow lets it through, it must be an extension, so
> from the standard language point of view the behavior is [undefined?]

They aren't extensions in the normal sense in that the
Standard specifically allows them; my best understanding
is that the Standard doesn't require them to be documented,
for example. In any case, the Standard explicitly provides
a way for such expressions to be accepted (without a diagnostic)
by a conforming implementation.