true

When asking about common undefined behavior in C, souls more enlightened than I referred to the strict aliasing rule.
What are they talking about?

7 upvote
  flag
@Ben Voigt: The aliasing rules are different for c++ and c. Why is this question tagged with c and c++faq. – MikeMB
2 upvote
  flag
@MikeMB: If you check the history, you'll see that I kept the tags the way they were originally, despite some other experts' attempt to change the question out from under existing answers. Besides, the language-dependence and version-dependence is a very important part of the answer to "What is the strict aliasing rule?" and knowing the differences is important to teams migrating code between C and C++, or writing macros for use in both. – Ben Voigt
6 upvote
  flag
@Ben Voigt: Actually - as far as I can tell - most answers only relate to c and not c++ also the wording of the question indicates a focus on the C-rules (or the OP was just not aware, that there is a difference). For the most part the rules and the general Idea are the same of course, but especially, where unions are concerned the answers don't apply to c++. I'm a little worried, that some c++ programmers will look for the strict aliasing rule and will just assume that everything stated here also applies to c++. – MikeMB
upvote
  flag
On the other hand, I agree that it is problematic to change the question after a lot of good answers have been posted and the issue is a minor one anyway. – MikeMB
1 upvote
  flag
@MikeMB: I think you'll see that the C focus on the accepted answer, making it incorrect for C++, was edited in by a third-party. That part probably should be revised again. – Ben Voigt

9 Answers 11

Strict aliasing is not allowing different pointer types to the same data.

This article should help you understand the issue in full detail.

4 upvote
  flag
You can alias between references and between a reference and a pointer as well. See my tutorial dbp-consulting.com/tutorials/StrictAliasing.html – Patrick
4 upvote
  flag
It is permitted to have different pointer types to the same data. Where strict aliasing comes in is when the same memory location is written through one pointer type and read through another. Also, some different types are permitted (e.g. int and a struct which contains an int). – M.M

Type punning via pointer casts (as opposed to using a union) is a major example of breaking strict aliasing.

1 upvote
  flag
See my answer here for the relevant quotes, especially the footnotes but type punning through unions has always been allowed in C although it was poorly worded at first. You my want to clarify your answer. – Shafik Yaghmour
upvote
  flag
@ShafikYaghmour: C89 clearly allowed implementers to select the cases in which they would or would not usefully recognize type punning through unions. An implementation could, for example, specify that for a write to one type followed by a read of another to be recognized as type punning, if the programmer did either of the following between the write and the read: (1) evaluate an lvalue containing the union type [taking the address of a member would qualify, if done at the right point in the sequence]; (2) convert a pointer to one type into a pointer to the other, and access via that ptr. – supercat
upvote
  flag
@ShafikYaghmour: An implementation could also specify e.g. that type punning between integer and floating-point values would only work reliably if code executed an fpsync() directive between writing as fp and reading as int or vice versa [on implementations with separate integer and FPU pipelines and caches, such a directive might be expensive, but not as costly as having the compiler perform such synchronization on every union access]. Or an implementation could specify that the resulting value will never be usable except in circumstances using Common Initial Sequences. – supercat
upvote
  flag
@ShafikYaghmour: Under C89, implementations could forbid most forms of type punning, including via unions, but the equivalence between pointers to unions and pointers to their members implied that type punning was allowed in implementations that didn't expressly forbid it. – supercat

The best explanation I have found is by Mike Acton, Understanding Strict Aliasing. It's focused a little on PS3 development, but that's basically just GCC.

From the article:

"Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias each other.)"

So basically if you have an int* pointing to some memory containing an int and then you point a float* to that memory and use it as a float you break the rule. If your code does not respect this, then the compiler's optimizer will most likely break your code.

The exception to the rule is a char*, which is allowed to point to any type.

5 upvote
  flag
So what is the canonical way to legally use the same memory with variables of 2 different types? or does everyone just copy? – jiggunjer
4 upvote
  flag
But then you can't have unions. – curiousguy
3 upvote
  flag
Mike Acton's page is flawed. The part of "Casting through a union (2)", at least, is downright wrong; the code he claims is legal is not. – davmac
4 upvote
  flag
@davmac: The authors of C89 never intended that it should force programmers to jump through hoops. I find thoroughly bizarre the notion that a rule that exists for the sole purpose of optimization should be interpreted in such fashion as to require programmers to write code that redundantly copies data in the hopes that an optimizer will remove the redundant code. – supercat
upvote
  flag
@Antonio: The link works for me. Maybe a temporary or regional issue. If the problem occurs again, please link to the latest snapshot in Internet Archive. – Palec
upvote
  flag
@Palec I still get "404 Not Found"... – Antonio
upvote
  flag
@Antonio: Just checked again, and when I click the link in this answer, I get the right article, no 404. Firefox 56 on Windows 10. No idea where this might have gone wrong for you. – Palec
upvote
  flag
@Palec Pretty amazing, must be something with my work network. Indeed on my Android phone it loads. – Antonio
upvote
  flag
@curiousguy: "Can't have unions"? Firstly, the original/primary purpose of unions is not in any way related to aliasing at all. Secondly, the modern language spec explicitly permits using unions for aliasing. The compiler is required to notice that a union is used and treat the situation is a special way. – AnT
upvote
  flag
@AnT "the original/primary purpose of unions is not in any way related to aliasing at all" What? That's the only purpose of a union. That the definition of a union: different objects at the same address. – curiousguy
1 upvote
  flag
@curiousguy: False. Firstly, the original conceptual idea behind unions was that at any moment there's only one member object "active" in the given union object, while the others simply don't exist. So, there are no "different objects at the same address" as you seem to believe. Secondly, aliasing violations everyone is talking about is about accessing one object as a different object, not about simply having two objects with the same address. As long as there is no type-punning access, there no problem. That was the original idea. Later, type-punning through unions was allowed. – AnT
upvote
  flag
There are other contexts in C language which might produce "different objects at the same address". For example, declaring int a[2]; double b; might result in the "imaginary" array element a[2] having the same address as b. The language does not consider this a problem. You just have to remember that only b is accessible, while a[2] can only be "pointed to", but cannot be accessed. – AnT
upvote
  flag
@AnT Then how do you write to a member of a union B after a member A has been written? – curiousguy
upvote
  flag
@curiousguy: By definition, writing into a union member "activates" that member and "deactivates" all other members. – AnT
upvote
  flag
@AnT And how would you do that? Is there an operator "activate" in C? – curiousguy
upvote
  flag
upvote
  flag
The link is broken. Archived link: web.archive.org/web/20171221170757/http://… – alfC
up vote 442 down vote accepted

A typical situation you encounter strict aliasing problems is when overlaying a struct (like a device/network msg) onto a buffer of the word size of your system (like a pointer to uint32_ts or uint16_ts). When you overlay a struct onto such a buffer, or a buffer onto such a struct through pointer casting you can easily violate strict aliasing rules.

So in this kind of setup, if I want to send a message to something I'd have to have two incompatible pointers pointing to the same chunk of memory. I might then naively code something like this:

struct Msg
{
    unsigned int a;
    unsigned int b;
};

void SendWord(uint32_t);

int main()
{
    // Get a 32-bit buffer from the system
    uint32_t* buff = malloc(sizeof(Msg));

    // Alias that buffer through message
    Msg* msg = (Msg*)(buff);

    // Send a bunch of messages    
    for (int i =0; i < 10; ++i)
    {
        msg->a = i;
        msg->b = i+1;
        SendWord(buff[0]);
        SendWord(buff[1]);   
    }
}

The strict aliasing rule makes this setup illegal: dereferencing a pointer that aliases another of an incompatible type is undefined behavior. Unfortunately, you can still code this way, maybe get some warnings, have it compile fine, only to have weird unexpected behavior when you run the code.

(GCC appears somewhat inconsistent in its ability to give aliasing warnings, sometimes giving us a friendly warning and sometimes not.)

To see why this behavior is undefined, we have to think about what the strict aliasing rule buys the compiler. Basically, with this rule, it doesn't have to think about inserting instructions to refresh the contents of buff every run of the loop. Instead, when optimizing, with some annoyingly unenforced assumptions about aliasing, it can omit those instructions, load buff[0] and buff[1] into CPU registers once before the loop is run, and speed up the body of the loop. Before strict aliasing was introduced, the compiler had to live in a state of paranoia that the contents of buff could change at anytime from anywhere by anybody. So to get an extra performance edge, and assuming most people don't type-pun pointers, the strict aliasing rule was introduced.

Keep in mind, if you think the example is contrived, this might even happen if you're passing a buffer to another function doing the sending for you, if instead you have.

void SendMessage(uint32_t* buff, size_t size32)
{
    for (int i = 0; i < size32; ++i) 
    {
        SendWord(buff[i]);
    }
}

And rewrote our earlier loop to take advantage of this convenient function

for (int i =0; i < 10; ++i)
{
    msg->a = i;
    msg->b = i+1;
    SendMessage(buff, 2);
}

The compiler may or may not be able to or smart enough to try to inline SendMessage and it may or may not decide to load or not load buff again. If SendMessage is part of another API that's compiled separately, it probably has instructions to load buff's contents. Then again, maybe you're in C++ and this is some templated header only implementation that the compiler thinks it can inline. Or maybe it's just something you wrote in your .c file for your own convenience. Anyway undefined behavior might still ensue. Even when we know some of what's happening under the hood, it's still a violation of the rule so no well defined behavior is guaranteed. So just by wrapping in a function that takes our word delimited buffer doesn't necessarily help.

So how do I get around this?

  • Use a union. Most compilers support this without complaining about strict aliasing. This is allowed in C99 and explicitly allowed in C11.

    union {
        Msg msg;
        unsigned int asBuffer[sizeof(Msg)/sizeof(unsigned int)];
    };
    
  • You can disable strict aliasing in your compiler (f[no-]strict-aliasing in gcc))

  • You can use char* for aliasing instead of your system's word. The rules allow an exception for char* (including signed char and unsigned char). It's always assumed that char* aliases other types. However this won't work the other way: there's no assumption that your struct aliases a buffer of chars.

Beginner beware

This is only one potential minefield when overlaying two types onto each other. You should also learn about endianness, word alignment, and how to deal with alignment issues through packing structs correctly.

13 upvote
  flag
I am coming after the battle it seems.. may unsigned char* be used far char* instead ? I tend to use unsigned char rather than char as the underlying type for byte because my bytes are not signed and I don't want the weirdness of signed behavior (notably wrt to overflow) – Matthieu M.
28 upvote
  flag
@Matthieu: Signedness makes no difference to alias rules, so using unsigned char * is okay. – Thomas Eding
upvote
  flag
Why not SendToStupidApi(&msg->a)? – Ben Voigt
14 upvote
  flag
Isn't it undefined behaviour to read from an union member different from the last one written to? – R. Martinho Fernandes
upvote
  flag
@Doug T.: as I understand, the same problem is on receiving side too when StupidAPI casts back to Msg, right? And how using char* instead of unsigned int* would improve this? – Andriy Tylychko
1 upvote
  flag
-1 The code examples in the answer code won't compile as C99. – Cheers and hth. - Alf
20 upvote
  flag
Bollocks, this answer is completely backwards. The example it shows as illegal is actually legal, and the example it shows as legal is actually illegal. – R. Martinho Fernandes
upvote
  flag
@R.MartinhoFernandes I reworked this answer quite a bit to hopefully be more accurate and clarify things. In the original example it was assumed that SendToStupidApi would dereference the type punned pointer thus violating strict aliasing. I also took out the reinterpret_cast that is of course not C. And added a note that the union solution violates the spec but is supported by most compilers. – Doug T.
upvote
  flag
+1 for word allignment link....!!it helped me lot – Jeegar Patel
upvote
  flag
I don't think the union solution violates the intent of C99, and it does not violate C11 (it was removed from appendix J). See: open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm – ninjalj
3 upvote
  flag
As per my comment to //allinonescript.com/questions/6359629/…: C99 says that the bytes that don't correspond to the last stored member take unspecified values (potentially implying that it's legal to read from members which share all its bytes with the last stored member), but then lists reading from a member other than the last stored as UB. C1X changes that, defining as UB reading from bytes that do not correspond to the last stored member. – ninjalj
upvote
  flag
Great answer. I have a similar question where I had to alias a struct onto a char array because I had to portably ensure alignment of the struct's memory. //allinonescript.com/questions/14170010/… – Pavel
1 upvote
  flag
This answer does not define what strict aliasing is. I also think this example is not correct, since the memory is being accessed by a struct that contains compatible types to the effective type of the memory (unsigned, assuming 32 bit values everywhere). – hdante
upvote
  flag
The answer tells using char* avoids this problem. Doesn't also passing the buffer as a void* parameter of a function solve this problem? – Nubok
4 upvote
  flag
@nubok: see the answer by Ben Voigt. The relevant paragraph of the C standard explicitly says 'character type' and does not mention 'void' or 'void pointer' at all. – Jonathan Leffler
7 upvote
  flag
Your uint32_t* buff = malloc(sizeof(Msg)); and the subsequent union unsigned int asBuffer[sizeof(Msg)]; buffer declarations will have different sizes and neither is correct. The malloc call is relying on the 4 byte alignment under the hood (don't do it) and the union will be 4 times bigger than it needs to be... I understand that it is for clarity but it bugs me none-the-less... – nonsensickle
upvote
  flag
Isn't the "wrong" example on top well defined if uint32_t is a typedef for unsigned int? Because Msg qualifies for "an aggregate or union type that includes one of the aforementioned types [here: uint32_t. -ps] among its elements", doesn't it? – Peter A. Schneider
upvote
  flag
I can't stand the fact that char b[4]; *(int*)b=0; does not work but int* i=*(int*)b; *i=0; does. I am calling for creating an anti strict aliasing movement where optimizer is forcing people to add extra declarations to avoid the issue. I understand that there can be some special case where warning actually should be an error but in the example above? come on! I am working on an old code that did not have this problem before, and I have hundreds of places to 'fix' (make less readable.) – Grzegorz
4 upvote
  flag
@Grzegorz: So use -fno-strict-aliasing when you compile, but then you aren't using C (or C++). Anyway, both pieces of code in your comment are wrong. The second won't even compile, since you have mismatching indirection in an initialization. – Ben Voigt
5 upvote
  flag
This example is unclear. It shows unsigned int being aliased as uint32_t. However it is implementation-defined whether unsigned int is compatible with uint32_t. If uint32_t is a typedef for unsigned int then the code actually has no strict aliasing violation, otherwise it does. I'd suggest editing it so that the two types are clearly incompatible. – M.M
1 upvote
  flag
I think you need to reconsider your example, if uint32_t is unsigned int then given my answer here there should be no strict aliasing violation. – Shafik Yaghmour
3 upvote
  flag
To address @nonsensickle's observation, the union declaration should have this member declaration instead: unsigned int asBuffer[sizeof(Msg)/sizeof(unsigned int)]. – David R Tribble
upvote
  flag
@DavidRTribble Thanks for addressing it. May I also suggest to you to mention the other two options that this article talks about, i.e. memcpy and the restrict keyword, in your answer (for completeness)? Other than that, good work! Tough topic... – nonsensickle
1 upvote
  flag
@DougT. I appears I though you were sending msg instead of buff, which is violating strict aliasing( so the code is correct ). My bad :-////. – this
upvote
  flag
@this all is forgiven. This is a hard topic and I appreciate having an opportunity to double check :) – Doug T.
upvote
  flag
I think the issue of optimizations that may or may not be possible can actually be unhelpful sometimes. The reason I say this is the I have constructed code where such optimizations weren't possible anyway, and was still told that the code is UB. So maybe, the optimization story is part of the history of the strict aliasing rule; but today, the today the effect of the rule is more drastic. Casting a pointer, and immediately reading from it, will be undefined generally (except for the specific exceptions around unions and compatible types). – Aaron McDaid
upvote
  flag
@Grzegorz "I can't stand the fact that" source? – curiousguy
upvote
  flag
@curiousguy: I am afraid I do not understand the question :) Can you rephrase it, please? If it still matters, of course. – Grzegorz
1 upvote
  flag
@Grzegorz What makes you think that int* i=*(int*)b; *i=0; works? – curiousguy
3 upvote
  flag
The example has drifted over time, and it now needs to be cleaned up. SendWord is declared to take a pointer-to-uint32_t, but it is then called on a uint32_t. And the commend "// Get a 32-bit buffer from the system" precedes an allocation of sizeof(Msg), which will be 64 bits on most popular implementations. I can't tell which errors are intentional, to point out the violation of strict aliasing, and which are accidents. What is the intended meaning? – Chris Culter
upvote
  flag
This code is total garbage. The signature for main is wrong. It either needs to use struct Msg or add typedef struct Msg Msg; The type of Sendword()'s argument in wrong. It doesn't have the needed #includes. And older compilers won't accept the for ( int i ... construct. – John Hascall
upvote
  flag
@JohnHascall Thanks. You're right. I'd love to see it cleaned up. Its community wiki, do you have enough rep to edit it? – Doug T.
upvote
  flag
@ChrisCulter I believe my original intention many many years back was to have those unsigned ints be 32 bits. – Doug T.
upvote
  flag
@Doug T. I don't think I do – John Hascall
upvote
  flag
@DougT. what does "use a union" mean in your answer. I ask because searching i cannot find a definitive answer. As far as i can understand (And im not 100% sure on this) - It seems that if you cast to the union* it is still undefined and that in order for it to be defined you need to first copy the data to a union access it through the type-pun built into the union. – skimon
upvote
  flag
@skimon what you say sounds right to me (I learned about strict aliasing ~10 hrs ago, however). You need to access the data in the union directly from a union member, not an alias to the union member. If you read the -fstrict_aliasing flag in this gcc manual, it has an example of unions: gcc.gnu.org/onlinedocs/gcc-5.3.0/gcc.pdf on page 149. Of course this is just gcc and not the C or C++ standards. – yano

Strict aliasing doesn't refer only to pointers, it affects references as well, I wrote a paper about it for the boost developer wiki and it was so well received that I turned it into a page on my consulting web site. It explains completely what it is, why it confuses people so much and what to do about it. Strict Aliasing White Paper. In particular it explains why unions are risky behavior for C++, and why using memcpy is the only fix portable across both C and C++. Hope this is helpful.

2 upvote
  flag
"Strict aliasing doesn't refer only to pointers, it affects references as well" Actually, it refers to lvalues. "using memcpy is the only fix portable" Hear! – curiousguy
3 upvote
  flag
The "Another Broken version, referencing a twice" section of the paper makes no sense. Even if there were a sequence point, it would not give the right result. Perhaps you meant to use shift operators instead of shift assignments? But then the code is well-defined and does the right thing. – ybungalobill
3 upvote
  flag
Good paper. My take: (1) this aliasing-'problem' is an over-reaction to bad programming - trying to protect the bad programmer from his/her bad habits. If the programmer has good habits then this aliasing is just a nuisance and the checks can safely be turned off. (2) Compiler-side optimization should only be done in well-known cases and should when in doubt strictly follow the source-code; forcing the programmer to write code to cater for the compiler's idiosyncrasies is, simply put, wrong. Even worse to make it part of the standard. – slashmais
1 upvote
  flag
@slashmais: I suspect that much of the horribleness of modern C stems from a belief that it is bad to add new directives and features to a language to tell a compiler what inferences it should make, but better to have the use of certain language features invoke inferences which, while consistent with the baseline requirements for what compilers must do, would break code that relies upon common and useful extensions that many implementations had historically provided at little or no cost. – supercat
2 upvote
  flag
@slashmais (1) "is an over-reaction to bad programming" Nonsense. It is a rejection of the bad habits. You do that? You pay the price: no guarantee for you! (2) Well known cases? Which ones? The strict aliasing rule should be "well known"! – curiousguy
upvote
  flag
@supercat No, modern C is the result of an incompetent re-design by people with no understanding of compiler design, optimization, or the formalism of a specification. It looks like a train crash. – curiousguy
upvote
  flag
@curiousguy: Reading your last comments to me and slashmais I'm not quite clear what you're trying to say about which versions of C. Perhaps we could discuss in a chat session? Protocol specifications make key distinctions which are missing in the C standard with regard to what compilers must provide vs what programs may legitimately rely upon, and between things implementations may do if convenient, things they should do if at all practical, and things they must do to be regarded as compliant On the other hand, C was written I think before such specs became common. – supercat
1 upvote
  flag
@curiousguy: While I'm not privvy to all the discussions leading up to various versions of the Standard, I suspect a major problem was a political need to avoid declaring any members' implementations "non-normative". It's useful to have a language which could be implemented on a sign-magnitude machine in such a fashion as to allow it to run a substantial fraction of code written in C. It's far less useful to say that because the meaning of left-shifting a negative number or casting an oversized unsigned value to int` is not well-defined on such machines, the Standard should impose... – supercat
upvote
  flag
...no requirements on the behavior of such operations even on two's-complement machines. Further, there are many case where code which will work pre-2009 compilers totalling 99%+ of the market can be faster and more readable than any code which must meet the same requirements when fed into any actual or theoretical Standard-compliant compiler. Failure to define a normative standard for behaviors which were widely-supported is detrimental to both source-code readability and machine-code efficiency--ironic given that the supposed justification for hyper-modernism is "optimization". – supercat
1 upvote
  flag
@curiousguy: Having cleared up a few points of confusion, it is clear that the C language with the aliasing rules makes it impossible for programs to implement type-agnostic memory pools. Some kinds of program can get by with malloc/free, but others need memory management logic better tailored to the tasks at hand. I wonder why the C89 rationale used such a crummy example of the reason for the aliasing rule, since their example makes it seem like the rule won't pose any major difficulty in performing any reasonable task. – supercat
upvote
  flag
@supercat It's a very unsettling conclusion. – curiousguy
upvote
  flag
@curiousguy, most compiler suites out there are including -fstrict-aliasing as default on -O3 and this hidden contract is forced on the users who've never heard of the TBAA and wrote code like how a system programmer might. I don't mean to sound disingenuous to system programmers, but this kind of optimization should be left outside of default opt of -O3 and should be an opt-in optimization for those that know what TBAA is. It is not fun looking at compiler 'bug' that turns out to be user code violating TBAA, especially tracking down the source level violation in user code. – kchoi
upvote
  flag
@kchoi: At minimum, compilers should by default recognize certain patterns that indicate that aliasing is likely; if a T* is cast to a U*, for example, ensure that any operations on T* which precede the cast also precede any uses of the resulting U*, and any operations on T* that follow the cast also follow any uses of the resulting U* which occurs before them. In some cases that would block what might otherwise have been useful optimizations, but it would be far more likely to make code work more cheaply than if the code had to use char* and the compiler had to... – supercat
upvote
  flag
...consequently expect that the U* might alias anything, rather than only having to expect that within a certain range of code it might access a T. I really doubt the authors of the C89 Standard would have taken seriously anyone who warned that unless they explicitly authorize such a pattern (using the cast pointer strictly between the next and subsequent access with the original type), compilers would optimize them out even on platforms where they would otherwise be useful, but unfortunately such obtuse behavior is now fashionable. – supercat

This is the strict aliasing rule, found in section 3.10 of the C++03 standard (other answers provide good explanation, but none provided the rule itself):

If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.

C++11 and C++14 wording (changes emphasized):

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type similar (as defined in 4.4) to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.

Two changes were small: glvalue instead of lvalue, and clarification of the aggregate/union case.

The third change makes a stronger guarantee (relaxes the strong aliasing rule): The new concept of similar types that are now safe to alias.


Also the C wording (C99; ISO/IEC 9899:1999 6.5/7; the exact same wording is used in ISO/IEC 9899:2011 §6.5 ¶7):

An object shall have its stored value accessed only by an lvalue expression that has one of the following types 73) or 88):

  • a type compatible with the effective type of the object,
  • a qualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
  • a character type.

73) or 88) The intent of this list is to specify those circumstances in which an object may or may not be aliased.

5 upvote
  flag
Ben, as people are often directed here, I have allowed myself to add the reference to the C standard too, for the sake of completeness. – Kos
upvote
  flag
@Kos: That's cool, thanks! Can you also comment on whether strict aliasing was required by C89/90? (I seem to recall not, that it was introduced at the same time as the restrict keyword, but I'm not sure). – Ben Voigt
1 upvote
  flag
Look at the C89 Rationale cs.technion.ac.il/users/yechiel/CS/C++draft/rationale.pdf section 3.3 which talks about it. – Patrick
upvote
  flag
How can you use an lvalue of class type? – curiousguy
upvote
  flag
@curiousguy: Any way that class allows. Seriously, I'm not going to write you an entire C++ book listing all the things that you can do with a class instance. One notable thing you can't do with an lvalue of class type is bind an rvalue reference to it. – Ben Voigt
upvote
  flag
@BenVoigt I am just asking for one example. – curiousguy
upvote
  flag
@curiousguy: In std::cout << "Hello World.\n";, std::cout is an lvalue with class type. – Ben Voigt
upvote
  flag
@BenVoigt Are you saying "section 3.10 of the C++03 standard" applies to std::cout? – curiousguy
upvote
  flag
Yes it applies. – Ben Voigt
1 upvote
  flag
If one has an lvalue of a structure type, takes the address of a member, and passes that to a function that uses it as a pointer to the member type, would that be regarded as accessing an object of the member type (legal), or an object of the structure type (forbidden)? A lot of code assumes it's legal to access structures in such fashion, and I think a lot of people would squawk at a rule which was understood as forbidding such actions, but it's unclear what the exact rules are. Further, unions and structures are treated the same, but sensible rules for each should be different. – supercat
1 upvote
  flag
@supercat: The way the rule for structures is worded, the actual access is always to the primitive type. Then access via a reference to the primitive type is legal because the types match, and access via a reference to the containing structure type is legal because it's specially permitted. – Ben Voigt
upvote
  flag
@BenVoigt: Under that interpretation, if S1 and S2 are structures with int x; as their first field, and which require nothing coarser than int alignment, then given void blah(S1 *p1, S2, *p2);` a compiler would not be allowed to make any assumptions about aliasing between p1->x and p2->x. because they could both identify storage of type int. I don't think that's what was intended. – supercat
upvote
  flag
@supercat: Check out "common initial subsequence". Aliasing between p1->x and p2->x is perfectly legal. – Ben Voigt
upvote
  flag
@BenVoigt: Given union { struct S1 s1; struct S2 s2; } u;, if both structures start with int x, then an assignment to u.s1.x is also an assignment to u.s1.x. That doesn't imply that they legitimately alias when not accessed through union u. – supercat
upvote
  flag
@supercat: So you claim that it would be invalid to call blah(&u.s1, &u.s2)? By what rule? – Ben Voigt
upvote
  flag
@BenVoigt: Given typedef struct { int x; float y;} S1; typedef struct { int x; int y;} S2; typedef union { S1 s1; S2 s2;} U; void wow1(S1 *p1, S2 *p2) { p1->x++; p2->x^=1; p1->x--; p2->x^=1; } gcc will compile the function as a do-nothing-and-return. – supercat
upvote
  flag
@BenVoigt: If gcc sees the definition of wow1 when it compiles a call to wow(&u->s1, &u->s2); it produces weird behavior which is consistent with neither the recognized-aliasing nor the non-aliasing case. – supercat
upvote
  flag
Hmm, I see that the common initial sequence rule only allows reading. So we could do bar(S1* p1, const S2* p2) { p1->x = p2->x+1; p1->x = p2->x-1; } and see what happens. Could be that gcc doesn't implement common initial sequence correctly. – Ben Voigt
upvote
  flag
@supercat: Also, why are you using typedef? We're talking about C++ aren't we? – Ben Voigt
1 upvote
  flag
@BenVoigt: I don't think the common initial sequence works unless accesses are done via the union. See goo.gl/HGOyoK to see what gcc is doing. If accessing an lvalue of union type via an lvalue of a member type (not using union-member-access operator) was legal, then wow(&u->s1,&u->s2) would need to be legal even when a pointer is used to modify u, and that would negate most optimizations that the aliasing rule was designed to facilitate. – supercat
1 upvote
  flag
@supercat: Not really, common initial sequence only allows aliasing int S1::x with int S2::x and not float S1::y with int S2::y. That's still a very powerful optimization rule. – Ben Voigt
upvote
  flag
@BenVoigt: My focus is on C, though gcc behavior here is identical in both C and C++ modes. In C++ the proper way to create structures with a recognized common initial subsequence would be to use inheritance, which would clearly be 100% legal, but that's not an option in C. – supercat
upvote
  flag
@supercat: Actually, you couldn't use inheritance and the union. Common initial sequence rule only applies to standard-layout types, which is broken by inheritance. – Ben Voigt
upvote
  flag
@BenVoigt: Though even in C++, I think that if S1 and S2 shared a common base SB, it would be legal to alias an S1* and an SB*, or an S2* and an SB*, but not to alias an S1* with an S2* even if code was only using members common to both. – supercat
upvote
  flag
@BenVoigt: Is it not possible to use inheritance with PODS? The only case where a compiler would have to store any type-related information when inheriting from a PODS and not adding any virtual members would be if it were legal to use delete on a base-type pointer to eliminate a derived-type object, and I don't think that's legal, is it? – supercat
1 upvote
  flag
@supercat: The rule is that a class that singly-inherits a standard-layout type and has no non-static data members of its own is also standard-layout. Or if it inherits an empty base class, the derived type can add non-static data members and still be standard-layout. But as soon as you have non-static data members at multiple different inheritance levels, you lose standard-layout. – Ben Voigt
upvote
  flag
@BenVoigt: My suspicion is that the rules almost "work", but that the rule would be useless for compilers if it didn't let them pretend some defined behavior was UB, and would make the language almost useless for programmers if compilers didn't treat some forms of UB predictably. – supercat
upvote
  flag
@BenVoigt: Hmm... that seems a shame, since there should be a useful way of creating a type whose layout was equivalent to struct derived1 { base1 b; int anothermember;} and allowing it to be used as either a base1 or ` derived1, without having to use b.` to access base members. – supercat
upvote
  flag
@supercat: Consider: struct B { double x; char y; }; struct D1 : B { char z; }; struct D2 { B b; char z; }; Is it legal for D1::z to overlap the padding in D1::B? What about D2::z and D2::b ? (Assume an 8-bit machine so that sizeof(double) == alignof(double) = 8) – Ben Voigt
upvote
  flag
@BenVoigt: Your point being that it is worthwhile to allow a derived type to be smaller than the sum of the base type and the new elements? I can see there would be benefit in that. Still, I would suggest that the usefulness of both C and C++ will be significantly hampered if there's no way to have a function accept a pointer to an arbitrary structure type with a known initial prefix. As noted, passing pointers to union members does not work, and there's no reason in the Standard why it should be legal to work with a pointer to even one union member if it's not legal to use more than that. – supercat
upvote
  flag
@supercat: Well, even with gcc's interpretation of strict aliasing, it is straightforward to "have a function accept a pointer to an arbitrary structure type with a known initial prefix". It's only when you have two such pointers, that overlap, that trouble arises. BTW my thought experiment is addressed here: //allinonescript.com/a/19950151/103167 – Ben Voigt
upvote
  flag
@BenVoigt: The authors of gcc are very keen to point out that even if today's version of gcc lets programmers get away with violating the rules, programmers have no right to complain if tomorrow's gcc punishes them. If the Standard says that code which receives a pointer to an arbitrary structure with a known initial prefix and uses it as a pointer to a different structure with the same prefix invokes UB, it doesn't matter what today's gcc does or does not do with such code. – supercat
upvote
  flag
@BenVoigt: It's too bad that the Standard didn't define separate "sizeof" and "strideof" operators; that wold have made it possible for an implementation allow objects to have a size which was not a multiple of their alignment. – supercat
upvote
  flag
@BenVoigt Does this //allinonescript.com/questions/34826036/… have undefined behaviour due to strict aliasing rule, or not, I am little confused – Suraj Jain
upvote
  flag
@SurajJain: No, that one is ok. The strict aliasing rule allows reading an object through both its actual type, and also through a char* (to see the individual bytes making up the object). – Ben Voigt

As addendum to what Doug T. already wrote, here is a simple test case which probably triggers it with gcc :

check.c

#include <stdio.h>

void check(short *h,long *k)
{
    *h=5;
    *k=6;
    if (*h == 5)
        printf("strict aliasing problem\n");
}

int main(void)
{
    long      k[1];
    check((short *)k,k);
    return 0;
}

Compile with gcc -O2 -o check check.c . Usually (with most gcc versions I tried) this outputs "strict aliasing problem", because the compiler assumes that "h" cannot be the same address as "k" in the "check" function. Because of that the compiler optimizes the if (*h == 5) away and always calls the printf.

For those who are interested here is the x64 assembler code, produced by gcc 4.6.3, running on ubuntu 12.04.2 for x64:

movw    $5, (%rdi)
movq    $6, (%rsi)
movl    $.LC0, %edi
jmp puts

So the if condition is completely gone from the assembler code.

upvote
  flag
if you add a second short * j to check() and use it ( *j = 7 ) then optimization disapear since ggc does not not if h and j are not actualy point to same value. yes optimisation is really smart. – philippe lhardy
2 upvote
  flag
To make things more fun, use pointers to types which aren't compatible but have the same size and representation (on some systems that's true of e.g. long long* and int64_t*). One might expect that a sane compiler should recognize that a long long* and int64_t* could access the same storage if they're stored identically, but such treatment is no longer fashionable. – supercat

According to the C89 rationale, the authors of the Standard did not want to require that compilers given code like:

int x;
int test(double *p)
{
  x=5;
  *p = 1.0;
  return x;
}

should be required to reload the value of x between the assignment and return statement so as to allow for the possibility that p might point to x, and the assignment to *p might consequently alter the value of x. The notion that a compiler should be entitled to presume that there won't be aliasing in situations like the above was non-controversial.

The authors of the Standard identified a few cases where aliasing might be used in code that should be nearly 100% portable, and mandated that compilers allow for aliasing in at least those cases. They made no attempt to pass judgment upon what constructs should be usable within code that is only intended to be usable on specific platforms, nor what constructs should be supported by quality implementations that claim to be suitable for systems programming on those platforms.

If a compiler for a particular platform indicates that it is intended for high-end number crunching applications, and a piece of operating system code for that platform malfunctions when fed to that compiler, that does not mean that the compiler is defective, nor does it mean that the code is defective. It merely means that the compiler and operating system code are not suitable for use with each other.

Unfortunately, some compiler writers point to the fact that the Standard doesn't require that all compilers recognize certain aliasing constructs as implying a judgment that all code using such constructs should be considered defective, even if the code does things which could not be done as efficiently any other way. If such compiler writers would recognize that the authors of the Standard have never tried to enumerate all the features and guarantees needed to make a compiler suitable for any particular purpose, they could shift their efforts toward figuring out how to make their compiler as useful as possible for a wide range of purposes, rather than trying to argue that the Standard doesn't require them to do so.

After reading many of the answers, I feel the need to add something:

Strict aliasing (which I'll describe in a bit) is important because:

  1. Memory access can be expensive (performance wise), which is why data is manipulated in CPU registers before being written back to the physical memory.

  2. If data in two different CPU registers will be written to the same memory space, we can't predict which data will "survive" when we code in C.

    In assembly, where we code the loading and unloading of CPU registers manually, we will know which data remains intact. But C (thankfully) abstracts this detail away.

Since two pointers can point to the same location in the memory, this could result in complex code that handles possible collisions.

This extra code is slow and hurts performance since it performs extra memory read / write operations which are both slower and (possibly) unnecessary.

The Strict aliasing rule allows us to avoid redundant machine code in cases in which it should be safe to assume that two pointers don't point to the same memory block (see also the restrict keyword).

The Strict aliasing states it's safe to assume that pointers to different types point to different locations in the memory.

If a compiler notices that two pointers point to different types (for example, an int * and a float *), it will assume the memory address is different and it will not protect against memory address collisions, resulting in faster machine code.

For example:

Lets assume the following function:

void merge_two_ints(int *a, int *b) {
  *b += *a;
  *a += *b;
}

In order to handle the case in which a == b (both pointers point to the same memory), we need to order and test the way we load data from the memory to the CPU registers, so the code might end up like this:

  1. load a and b from memory.

  2. add a to b.

  3. save b and reload a.

    (save from CPU register to the memory and load from the memory to the CPU register).

  4. add b to a.

  5. save a (from the CPU register) to the memory.

Step 3 is very slow because it needs to access the physical memory. However, it's required to protect against instances where a and b point to the same memory address.

Strict aliasing would allow us to prevent this by telling the compiler that these memory addresses are distinctly different (which, in this case, will allow even further optimization which can't be performed if the pointers share a memory address).

  1. This can be told to the compiler in two ways, by using different types to point to. i.e.:

    void merge_two_numbers(int *a, long *b) {...}
    
  2. Using the restrict keyword. i.e.:

    void merge_two_ints(int * restrict a, int * restrict b) {...}
    

Now, by satisfying the Strict Aliasing rule, step 3 can be avoided and the code will run significantly faster.

In fact, by adding the restrict keyword, the whole function could be optimized to:

  1. load a and b from memory.

  2. add a to b.

  3. save result both to a and to b.

This optimization couldn't have been done before, because of the possible collision (where a and b would be tripled instead of doubled).

upvote
  flag
with restrict keyword, at step 3, shouldn't it be save result to 'b' only? It sounds as if the result of the summation will be stored in 'a' as well. Does it 'b' need to be reloaded again? – NeilB
1 upvote
  flag
@NeilB - Yap you're right. We're only saving b (not reloading it) and reloading a. I hope it's clearer now. – Myst

Not the answer you're looking for? Browse other questions tagged or ask your own question.