Open In App

Strict Aliasing Rule in C with Examples

Improve
Improve
Like Article
Like
Save
Share
Report

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




// 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);
}


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




// 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;
}


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




#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;
}


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




#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;
}


Output:

11

Another example program that violates the strict aliasing rule:

CPP




#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;
}


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.



Last Updated : 08 Jul, 2020
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads