0

I've been reading some articles about the Strict Aliasing Rules for a couple of days. Here are my understandings:

  1. An object's effective type is the type of its declaration. If the object is an allocated memory, it does not have one until accessed by an lvalue with an effective type, which becomes the object's effective type.

  2. An access to the value of an object must be through a compatible type with its effective type.

After I thought I got this, I wanted to do a simple experiment to see if my compiler really warns about this when I deliberately break the rule. Here's my code:

int main(void) {
    unsigned char c[5] = {0x1, 0x2, 0x3, 0x4, 0x5};
    int i = *(int*)c;
    printf("%x\n", i);
    
    return 0;
}

To me this seems to be against the rule because c has an effective type of char and we are trying to access it with an int pointer.

But my gcc compiles just fine! Even with the highest constraining level (-Wstrict-aliasing) it does not give a single warning. But strangely, replacing the int with a float gives the expected response:

int main(void) {
    unsigned char c[5] = {0x1, 0x2, 0x3, 0x4, 0x5};
    float i = *(float*)c;
    printf("%f\n", i);
    
    return 0;
}

The compiler gives a warning for this code. (dereferencing type-punned pointer will breakk strict-aliasing rules [Wstrict-aliasing])

Does the first code really break the rules? I know casting any pointer into a char* type is fine, but is it true the other way around? Or is it just something gcc does not care so much for?

morimn
  • 513
  • 3
  • 10
  • 1
    From https://stackoverflow.com/a/98702/1491895: **The exception to the rule is a char*, which is allowed to point to any type.** – Barmar May 19 '21 at 16:36
  • 2
    *but is it true the other way around* - no, it is not. Strict aliasing rule exception for `char*` is only telling that an object of any type can be aliased as `char*` - that is an object is still of the original type, but can be accessed with `char*`. In your case, as you say, the effective type is array of `char*`, so accessing it as `int*` or `float*` is breaking the rule. The compiler should be warning about it as you expect, and I take the fact it doesn't as an omission, or some implementation specific thing - where the compiler has the behavior well defined. – Eugene Sh. May 19 '21 at 16:42
  • BTW, I don't see the warning for any of the snippets (with fixed code, as the original has errors) - gcc 10.3.0 – Eugene Sh. May 19 '21 at 16:48
  • @EugeneSh. I gave `-Wstrict-aliasing` and `-fstrict-alisasing` as options. Does that still give no warning? If so, that's really interesting! (Mine is 9.3.0) – morimn May 19 '21 at 16:51
  • Nope.. neither of these – Eugene Sh. May 19 '21 at 16:52
  • @EugeneSh. Thanks. Edited the post for my stupid error :) – morimn May 19 '21 at 16:54
  • 2
    gcc 4.8.5 with `-fstrict-alisasing` generates the warning for both. – dbush May 19 '21 at 17:05
  • `char` exception was added to allow memcpy like functions to be written not breaking any rules. – 0___________ May 19 '21 at 17:10
  • @0___________: Very little code--including most uses of `memcpy`, would rely upon the `char` exception if compilers treated an lvalue that was freshly visibly based upon a pointer to, or object of, a given type as being usable to access an object of that type, and treated the ability to recognize that something was based upon something else as a "quality of implementation" issue. I know of no evidence that the Committee would have expected any non-obtuse compiler to assume, given `float *fp;`, that a statement like `*(unsigned*)fp += 1;` couldn't possibly affect the value of a `float`. – supercat May 19 '21 at 17:18

1 Answers1

2

According to the published Rationale for the C Standard, the purpose of the constraint referred to as the "strict aliasing rule" was to avoid requiring that a compiler given something like:

int x;
int test(double *p)
{
  x = 1;
  *p = 2.0;
  return x;
}

allow for the possibility that the store to *p might affect the value of x, even though there is nothing in the code that would suggest that such a thing could happen. Because the Standard explicitly specifies that implementations may process constructs where it imposes no requirements "in a documented manner characteristic of the environment", the authors saw no need to fully enumerate all of the constructs they expected implementations to handle consistently. Consequently, in order to avoid rendering the language useless, every compiler must process meaningfully some constructs that violate the "aliasing" constraints as written.

From what I can tell, one of the ways that gcc does that is by applying the rules bidirectionally in some cases beyond those provided for by the constraint. The rules as written wouldn't require that an implementation allow for the possibility that an object of character-array type might be accessed using an lvalue of type int, but they also wouldn't require that implementations allow for the possibility that a structure object containing an array of integers might be accessed by dereferencing an int* such as that formed by the decay of the array in an expression like myStruct.intArray[2]. In cases where the authors of gcc recognize that treating a construct as a violation of the "aliasing" constraints would be silly, they will treat the construct as though it were not a constraint violation, and thus will not warn about it.

supercat
  • 77,689
  • 9
  • 166
  • 211