Strict Aliasing Rule in C with Examples

Aliasing: Aliasing refers to the situation where the same memory location can be accessed using different names.

For Example, if a function takes two pointers A and B which have the same value, then the name A[0] aliases the name B[0] i.e., we say the pointers A and B alias each other.

Below is the program to illustrate aliasing in C:

C

filter_none

edit
close

play_arrow

link
brightness_4
code

// C program to illustrate aliasing
#include <stdio.h>
  
// Function to add 10 to b
int gfg(int* a, int* b)
{
    *b = *b + 10;
    return *a;
}
  
// Driver Code
int main()
{
    // Given data
    int data = 20;
  
    // Function Call with aliasing
    int result = gfg(&data, &data);
  
    // Print the data
    printf("%d ", result);
}

chevron_right


Output:

30

Explanation:
In the above program, the value of data is 20. When this variable is aliased to function gfg() then the changes made to memory location referred by pointer ‘b’, got reflected in ‘a’ as well. This is done because they referred to the same memory location.



Strict Aliasing:
GCC compiler makes an assumption that pointers of different types will never point to the same memory location i.e., alias of each other. Strict aliasing rule helps the compiler to optimize the code. The Strict Aliasing rule is enabled by default on optimization levels 2 and beyond, and when one tries to compile a program with aforementioned details the compiler throws warnings, though the program still compiles and can be executed, the output of such a program remains unpredictable.

C

filter_none

edit
close

play_arrow

link
brightness_4
code

// C program to illustrate aliasing
#include <stdio.h>
  
// Function to change the value of
// ptr1 and ptr2
int foo(int* ptr1, int* ptr2)
{
    *ptr1 = 10;
    *ptr2 = 11;
    return *ptr1;
}
  
// Driver Code
int main()
{
    int data1 = 10, data2 = 20;
  
    // Function Call
    int result = foo(&data1, &data2);
  
    // Print result
    printf("%d ", result);
    return 0;
}

chevron_right


Output:

10

Explanation:
In the above code, the compiler can not optimize the code to directly return 10, since both pointers ptr1 and ptr2 could be an alias of each other. In that case, it would return 11, not 10.

Even if we are sure that both pointers are not an alias of each other, then also optimization could not be performed by the compiler. Now, to solve this problem strict aliasing rule has been introduced. It ensures compiler that pointers of different types can not be an alias of each other.

C

filter_none

edit
close

play_arrow

link
brightness_4
code

#include <stdio.h>
  
int foo(int* ptr1, long* ptr2)
{
    *ptr1 = 10;
    *ptr2 = 21.02;
    return *ptr1;
}
  
int main()
{
    int a = 11;
    long b = 20.02;
    int result = foo(&a, &b);
    printf("%d ", result);
    return 0;
}

chevron_right


Output:

10

Here code can be optimized directly to return 10 since the compiler knows for sure that both pointers would not be an alias of each other. Here, we can observe that the compilers can leverage strict-aliasing rules to generate better-optimized code.

Note: Since, both C and C++ allow casting between pointer types, which will eventually create aliases and thus, violate the compiler’s assumption.

C

filter_none

edit
close

play_arrow

link
brightness_4
code

#include <stdio.h>
  
int foo(int* ptr1, long* ptr2)
{
    *ptr1 = 10;
    *ptr2 = 11;
    return *ptr1;
}
  
int main()
{
    long a = 11.0;
    int result = foo((int*)&a, &a);
    printf("%d ", result);
    return 0;
}

chevron_right


Output:



11

Another example program that violates the strict aliasing rule:

CPP

filter_none

edit
close

play_arrow

link
brightness_4
code

#include <bits/stdc++.h>
using namespace std;
  
__uint32_t left(__uint32_t val)
{
    __uint32_t val_temp = val;
    // can't use static_cast<>,
    // not legal
    __uint16_t* ptr
        = (__uint16_t*)&val_temp;
    __uint16_t tmp = ptr[0];
    ptr[1] = ptr[0];
    ptr[0] = 0;
    return val_temp;
}
  
int main()
{
    __uint32_t val;
    val = 1;
    val = left(val);
    cout << val << endl;
}

chevron_right


Output:

65536

Explanation:

The above code simply left shifts the value of variable val by 16 bits, by swapping the lower 16 bits to the higher 16 bits and setting the higher 16 bits to 0, however here we use __uint16_t pointer ptr to create an alias of ‘val’, this violates the strict aliasing rule, and this may lead to different outputs on different levels of optimizations (i.e. O1, O2 and O3) on a compiler.

How To Bypass the Strict Aliasing Rules?:

Although the rules are implemented to prevent unexpected behavior, the rules can be bypassed by ignoring the warnings and explicitly using the  -fno-strict-aliasing tag while compiling, this ensures no rules are enforced even when level O3, O2 optimizations are applied.

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.




My Personal Notes arrow_drop_up

Efficient Student pursuing a Computer Science degree with experience collaborating with teammates to create projects that solve real-world problems efficiently Well-versed in Data Structures and Algorithms, with a strong understanding of Mathematics, profound love for Neural Nets and Deep Learning

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


Article Tags :
Practice Tags :


Be the First to upvote.


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.