Francois Grieu said:
Consider the following code:
typedef unsigned char byt;
struct {
byt fa[8];
byt fb[8];
} mys;
byt* const gp = mys.fa;
int f0(void) { return (int)(mys.fb-gp-2); }
int f1(void) { return (int)(mys.fb-2-gp); }
I recently encountered a platform where
f0() returns 6
but
f1() returns 262
I understand why the machine code generated for f1
produces that result, and what kind of optimization
triggers the generation of that code.
But I wonder exactly what chapter and verse (if any)
in the C standards makes f1 invoke UB; and if f0 is
safe or not.
Both are undefined behavior, for slightly different reasons
but essentially the same one, which is pointer arithmetic is
defined only within a single array. Adding an integer to
'mys.fb' is defined only when the integer's value is between
0 and 8, inclusive. Subtracting a pointer from 'mys.fb' is
defined only when the pointer being subtracted is in the
range of { &mys.fb[0] .. &mys.fb[8] }. The function f0()
violates the second of these conditions, the function f1()
violates the first. The key observation in both cases is
that use of 'mys.fb' limits the array in question to just
that member, not the entire struct. Similarly the variable
'gp' is limited to the member 'mys.fa', because that is
where its value came from.
Unfortunately the Standard does not spell out very precisely
which array governs any particular pointer being operated on
(at least not precisely enough IMO). However the case here
is addressed more or less directly in the Defect Reports
that concern this question. The paragraphs on pointer
arithmetic are 6.5.6 p8 and p9 in n1256, although that may
not help much because of the ambiguity as to which array
governs the restrictions.
There is a way to do what you want definedly, using
the offsetof() macro
typedef struct { byt fa[8]; byt fb[8]; } T;
char *p_mys = (char *) &mys;
char *p_fa = p_mys + offsetof( T, fa );
char *p_fb = p_mys + offsetof( T, fb );
ASSERT( (p_fb - p_fa) % sizeof (byt) == 0 );
int result_f0 = (p_fb - p_fa - 2) / sizeof (byt);
int result_f1 = (p_fb - 2 - p_fa) / sizeof (byt);
Both of the last two expressions are well defined. (Yes it
is possible to simplify the expressions given above, taking
advantage of 'byt' being a character type, and 'fa' being
the first member of the struct, but the example is meant to
illustrage a general pattern.)
Note that in principle it is possible for the ASSERT() to
fail if sizeof (byt) > 1. Of course it is highly unlikely
that this will ever occur but it's a good practice to put in
something as a reminder, especially in the general case
where there may be members between 'fa' and 'fb'. Depending
on just what it is you're trying to do you might want to
test the stronger condition 'p_fa + sizeof mys.fa == p_fb',
which I believe is what is expected to be the case.