When using gcc to write code for embedded systems, it is important to note that unlike many embedded-systems compilers which allow storage may be written as one type and read as another, and will treat integer overflow in at least somewhat predictable fashion, code which relies upon such behaviors is apt to break when compiled with gcc unless compiled using -fno-strict-aliasing
and -fwrapv
flags. For example, while the authors of the C Standard would have expected that the function
// Assume usmall is an unsigned value half the size of unsigned int
unsigned multiply(usmall x, usmall y) { return x*y; }
should be safe on two's-complement hardware platforms with silent wraparound on overflow, they didn't require compilers to implement it that way (I think they probably didn't expect that anyone who was writing a compiler for such a platform would be so obtuse as to do otherwise unless emulating some other platform). When compiled with gcc, however, that function may have unexpected side-effects.
Likewise, on many compilers, given e.g.
struct widget_header {uint16_t length; uint8_t type_id;};
struct acme_widget {uint16_t length; uint8_t type_id; uint8_t dat[5];};
struct beta_widget {uint16_t length; uint8_t type_id; uint32_t foo;};
a pointer to any of those types could be cast to widget_header; code
could then look at the type_id field and cast to a more specific type.
Such techniques will not always work on gcc, however; even if a union
declaration containing all three types is in scope, gcc will assume that
an access to a field of one of those types cannot possibly modify the
corresponding field in any other.
A more concrete example to show how gcc treats aliasing:
struct s1 { int x; };
struct s2 { int x; };
union { struct s1 v1; struct s2 v2; } u;
static int peek(void *p)
{
struct s1 *p1 = (struct s1*)p;
return *(int*)&p1->x;
}
static void setToFive(void *p)
{
struct s2 *p2 = (struct s2*)p;
*(int*)(&p2->x) = 5;
}
static int test1a(void *p, void *q)
{
struct s1 *p1 = (struct s1*)p;
if (peek(p)!=23) return 0;
setToFive(q);
return peek(p);
}
int test1(void)
{
struct s2 v2 = {23};
u.v2 = v2;
return test1a(&u.v1, &u.v2);
}
ARM gcc 4.8.2 generates
test1:
movw r3, #:lower16:u
movt r3, #:upper16:u
movs r2, #5
movs r0, #23
str r2, [r3]
bx lr
which stores a 5 into "u" and then returns 23 (assuming that the second call to peek
will return the same value as the first one, notwithstanding all of the pointer typecasts which should give a pretty clear indication to the compiler that something might possibly be aliased somewhere).