Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strict Aliasing and Unions in C

Consider the following C program (which is based on an example from this article):

#include <stdio.h>

short g(int *p, short *q) {
  short z = *q; *p = 10; return z;
}

int main(void) {
  union int_or_short { int x; short y; } u = { .y = 3 };
  int *p = &u.x;
  short *q = &u.y;
  short r = g(p, q);
  printf("%hd %d\n", r, u.x);
}

Does this program exhibit undefined behavior? Please explain by referring to specific passages in the C17 Standard.

like image 724
Steve Siegel Avatar asked Oct 28 '25 20:10

Steve Siegel


1 Answers

This is in fact undefined behavior, as the union definition is not visible in the function g and therefore the compiler may assume that the p and q parameters (which point to incompatible types) point to distinct objects.

There's a nearly identical example given in section 6.5.2.3p9:

EXAMPLE 3 The following is a valid fragment:

union {
    struct {
        int alltypes;
    } n;
    struct {
        int type;
        int intnode;
    } ni;
    struct {
        int type;
        double doublenode;
    } nf;
} u;
u.nf.type = 1;
u.nf.doublenode = 3.14;
/* ... */
if (u.n.alltypes == 1)
if (sin(u.nf.doublenode) == 0.0)
/* ... */

The following is not a valid fragment (because the union type is not visible within function f):

struct t1 { int m; };
struct t2 { int m; };
int f(struct t1 *p1, struct t2 *p2)
{
    if (p1->m < 0)
        p2->m = -p2->m;
    return p1->m;
}

int g()
{
    union {
        struct t1 s1;
        struct t2 s2;
    } u;
    /* ... */
    return f(&u.s1, &u.s2);
}

The second piece of code above is passing pointers to two members of the same union to a function where the union definition is not visible, followed by 1) a read of one member 2) a write of the other 3) a read of the first, just as in your example.

like image 137
dbush Avatar answered Oct 31 '25 12:10

dbush