3

Note: learning the strict aliasing rule. Please be patient.

Code sample (t935.c):

#include <stdio.h>

int f(int* pi, double* pd)
{
    *pi = 13;
    *pd = 7E-323;
    return *pi;
}

int main(void)
{
    union { int i; double d; } u;
    printf("%d\n", f(&u.i, &u.d));
    return 0;
}

Invocations:

$ gcc t935.c -Wall -Wextra -std=c11 -pedantic  && ./a.exe
14

$ gcc t935.c -Wall -Wextra -std=c11 -pedantic -O2 && ./a.exe
13

$ gcc t935.c -Wall -Wextra -std=c11 -pedantic -O2 -fno-strict-aliasing && ./a.exe
14

Question: does passing to a function two pointers pointed to members of the same union violate the strict aliasing rule?

The question is originated from Unions and type-punning.

UPD20210901

what would happen if the union type was defined at global scope?

For "union u is defined at global scope" (actually file scope) case both gcc and clang show the same results as reported above for gcc.

pmor
  • 5,392
  • 4
  • 17
  • 36
  • yes. the union is not visible inside the body of `f()`, what would happen if the `union` type was defined at global scope? – tstanisl Sep 01 '21 at 10:02
  • @tstanisl Not sure why or how the scope of the union has any bearing on this. The function sees only two pointers, and uses them separately: that they point to the 'same' place is irrelevant, is not? – Adrian Mole Sep 01 '21 at 10:08
  • there is a subtlety in the standard related to visibility of union and scrict aliasing rules, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65892 – tstanisl Sep 01 '21 at 10:17
  • This question also came up recently in https://stackoverflow.com/questions/68963247/why-are-the-results-of-this-code-different-with-and-without-fsanitize-undefine/68964520#comment121886036_68964520. Also see https://stackoverflow.com/questions/16804650/accessing-c-union-members-via-pointers – Nate Eldredge Sep 01 '21 at 13:17

1 Answers1

5

This is a defect in the C standard. See Defect Report 236.

The aliasing rule in 6.5 7 is intended to allow a compiler to assume pointers to two types that do not satisfy the rule refer to “different” objects, but the rule fails to adequately provide for union members (and also, per the defect report, objects in dynamically allocated memory). Taking the addresses of two union members may yield pointers to two types that may not alias according to the aliasing rule but that refer to the same memory. Thus, the C standard is broken in this regard, and the C committee has not corrected the problem.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Unfortunately, while DR#236 reached the right conclusion about the particular piece of code given, it did so based on a nonsensical rationale which treat Implementation-Defined and Undefined Behavior behavior as equivalent. Example 2 should be regarded as UB because the list of types that may be used to access a `union tag` does not include `int` nor `double`. Obviously a compiler should allow for the possibility that lvalues of such types may access an object of type `union tag` in contexts where either a compiler can see that an `int*` or `double*` is derived from an object of type... – supercat Sep 07 '21 at 21:59
  • `union tag`, or where an object is only accessed via `int*`, or only via `double*`, but the questions of when a compiler would notice such pointer derivation, or how broadly or narrowly it draws the contexts where aliasing would matter, were viewed as quality of implementation issues. The "Effective Type" rules are effectively a codification of the nonsensical rationale given in DR236. – supercat Sep 07 '21 at 22:02