sizeof() structure member

K

keith

I've been using this construct for ages, without ill effects:

#define msizeof(st, m) (sizeof(( (st *)(0) )->m))

Now I hear this gives undefined behavior? Is it true?
 
K

Keith Thompson

keith said:
I've been using this construct for ages, without ill effects:

#define msizeof(st, m) (sizeof(( (st *)(0) )->m))

Now I hear this gives undefined behavior? Is it true?

No. The operand of sizeof is not evaluated (unless it's a variable
length array, which doesn't apply here). Only the type of the
expression is considered.
 
K

Keith Thompson

Keith Thompson said:
No. The operand of sizeof is not evaluated (unless it's a variable
length array, which doesn't apply here). Only the type of the
expression is considered.

Note that this is similar to a common implementation of the offsetof
macro:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

This does invoke UB, but it happens to work on many (most?)
implementations. The standard doesn't say that the null pointer
won't actually be dereferenced, but most compilers will optimize
it out. It also makes assumptions about the representation of a null
pointer and the behavior of a conversion from pointer to size_t.
Since the definition is part of the implementation, the author is
free to depend on implementation-specific assumptions.

None of this applies to your msizeof macro, whose behavior is well
defined for any conforming implementation.
 
B

Barry Schwarz

I've been using this construct for ages, without ill effects:

#define msizeof(st, m) (sizeof(( (st *)(0) )->m))

Now I hear this gives undefined behavior? Is it true?

No. The operand of the sizeof operator is not evaluated, unless you
are using variable length arrays. The operand servers only to
identify a type which the compiler determines at compile time.
The implied dereference of the -> operator would be a run time
activity if the expression were evaluated, but it isn't.
 
R

Richard Tobin

#define msizeof(st, m) (sizeof(( (st *)(0) )->m))

Now I hear this gives undefined behavior? Is it true?
[/QUOTE]
No. The operand of the sizeof operator is not evaluated, unless you
are using variable length arrays.

Can someone point me to the place in the standard which allows the
above, but prohibits it if "0" is replaced by "main"? Evidently
being a function rather than an object pointer is not an error of
evaluation, while being a null pointer is. More generally, what
has to be true of an expression that is not evaluated? Does it
just have to satisfy the Constraints?

-- Richard
 
K

Keith Thompson

No. The operand of the sizeof operator is not evaluated, unless you
are using variable length arrays.

Can someone point me to the place in the standard which allows the
above, but prohibits it if "0" is replaced by "main"?[/QUOTE]

The place in the standard that permits the above is the set of all the
syntax rules and constraints that it doesn't violate.

As for replacing "0" by "main", that doesn't violate any syntax rule
or contraint either, and in fact it seems to work. The behavior of
converting main (a function pointer, after it decays) to st* is
undefined, but I don't know of any constraint that the conversion
violates.

For example this program:

#include <stdio.h>

#define msizeof(st, m) (sizeof(( (st *)(main) )->m))

int main(void)
{
struct foo { int x; int y; };
printf("msizeof(struct foo, y) = %d\n", (int)msizeof(struct foo, y));
return 0;
}

prints "4" on my system, though with "-pedantic" it produces a warning:

c.c: In function `main':
c.c:8: warning: ISO C forbids conversion of function pointer to object pointer type

I believe this warning is incorrect.

Note that C99 6.3.2.3, which describes pointer conversions, says that
pointers to object or incomplete types may be converted back and forth,
as may pointers to (possibly different) function types. But that's
not a constraint.

C99 6.5.4, which describes the cast operator, has the following constraints,
none of which are violated here:

Unless the type name specifies a void type, the type name shall
specify qualified or unqualified scalar type and the operand shall
have scalar type.

Conversions that involve pointers, other than where permitted by
the constraints of 6.5.16.1, shall be specified by means of an
explicit cast.
Evidently
being a function rather than an object pointer is not an error of
evaluation, while being a null pointer is. More generally, what
has to be true of an expression that is not evaluated? Does it
just have to satisfy the Constraints?

Constraints and syntax rules, yes. There is no runtime behavior, so
it doesn't matter that the behavior is undefined.
 
S

spinoza1111

[email protected] (Richard Tobin) said:
The place in the standard that permits the above is the set of all the
syntax rules and constraints that it doesn't violate.

As for replacing "0" by "main", that doesn't violate any syntax rule
or contraint either, and in fact it seems to work.  The behavior of
converting main (a function pointer, after it decays) to st* is
undefined, but I don't know of any constraint that the conversion
violates.

For example this program:

#include <stdio.h>

#define msizeof(st, m) (sizeof(( (st *)(main) )->m))

int main(void)
{  
    struct foo { int x; int y; };
    printf("msizeof(struct foo, y) = %d\n", (int)msizeof(struct foo, y));
    return 0;

}

prints "4" on my system, though with "-pedantic" it produces a warning:

c.c: In function `main':
c.c:8: warning: ISO C forbids conversion of function pointer to object pointer type

I believe this warning is incorrect.

Note that C99 6.3.2.3, which describes pointer conversions, says that
pointers to object or incomplete types may be converted back and forth,
as may pointers to (possibly different) function types.  But that's
not a constraint.

C99 6.5.4, which describes the cast operator, has the following constraints,
none of which are violated here:

    Unless the type name specifies a void type, the type name shall
    specify qualified or unqualified scalar type and the operand shall
    have scalar type.

    Conversions that involve pointers, other than where permitted by
    the constraints of 6.5.16.1, shall be specified by means of an
    explicit cast.


Constraints and syntax rules, yes.  There is no runtime behavior, so
it doesn't matter that the behavior is undefined.

--
Keith Thompson (The_Other_Keith) (e-mail address removed)  <http://www.ghoti.net/~kst>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

A fog, a mist, confusion complete
With this is C discourse quite replete
'Tis said indeed, what a tangl'd Web we weave
When we practise thus to deceive
And in defense of money and power
The plot thickens, hour by hour.
Taking 'vantage of oddities
Knowledge of quips and quiddities
Drives out the scholar
So that vendors can make a dollar.
The code works, the user's happy
Just change the monkey's nappy.
Naught's recognized to be true,
Unless accompanied by a heartfelt "screw you".
Screw the truth, Ruth, we're playing games
Schildt's the target of our scorns and shames.

"This prophecie Merlin shall make, for I liue before his time."
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top