C++

What does the restrict keyword mean in C

19 September 2026 · 12 min read

What does the restrict keyword mean in C

In the world of C++, optimizing code for performance is a constant pursuit. One tool that can significantly aid in this endeavor, although often overlooked, is the restrict keyword. Understanding what the restrict keyword means in C++ is crucial for developers aiming to write high-performance code, especially in areas like scientific computing, game development, and systems programming. The restrict keyword is a type qualifier that informs the compiler that a pointer provides exclusive access to the object it points to within a specific scope. This allows the compiler to make certain optimizations, such as vectorization and loop unrolling, that would otherwise be unsafe. This article delves into the intricacies of restrict, exploring its purpose, usage, and impact on code optimization, and its related concepts like aliasing and memory management.

Understanding the restrict Keyword in C++

The restrict keyword, formally introduced in C99 and later adopted by C++, is a declaration that a pointer is the sole means of accessing a particular memory region within a certain scope. In simpler terms, if you declare a pointer as restrict, you’re promising the compiler that no other pointer will be used to modify the same memory location during the pointer’s lifetime. This “promise” allows the compiler to perform aggressive optimizations because it doesn’t have to worry about aliasing – the possibility that multiple pointers might refer to the same memory location. Without restrict, the compiler must assume that aliasing could occur, which limits its ability to perform certain transformations on the code.

Consider a scenario involving two pointers, a and b, both pointing to elements within an array. If a and b are not declared restrict, the compiler must assume they could point to the same memory location. This uncertainty prevents optimizations like loop unrolling or vectorization because modifying the value pointed to by a could unexpectedly affect the value pointed to by b. By declaring a and b as restrict, you are explicitly stating that they will never point to the same location, enabling the compiler to safely perform these optimizations. According to a study by Intel, the use of restrict can lead to performance improvements of up to 30% in certain numerical algorithms [^1^].

However, it’s crucial to use restrict correctly. If you violate the promise you make to the compiler – that is, if two restrict pointers do alias – the behavior of your program is undefined. This can lead to incorrect results, crashes, or other unpredictable issues. Therefore, restrict should only be used when you are absolutely certain that the pointers involved do not alias. In essence, the restrict keyword provides a contract between the programmer and the compiler: the programmer guarantees exclusive access, and the compiler, in turn, promises to optimize the code more aggressively.

How restrict Enables Compiler Optimizations

The primary benefit of using the restrict keyword lies in the compiler optimizations it enables. When the compiler knows that a pointer is the only way to access a particular memory region, it can perform transformations that would otherwise be unsafe due to the possibility of aliasing. These optimizations can significantly improve the performance of code, particularly in computationally intensive tasks.

One common optimization enabled by restrict is vectorization. Vectorization involves processing multiple data elements simultaneously using Single Instruction, Multiple Data (SIMD) instructions. If the compiler cannot be sure that pointers do not alias, it cannot safely vectorize loops that involve memory access through those pointers. With restrict, the compiler can confidently vectorize such loops, leading to substantial performance gains. Another optimization is loop unrolling, where a loop is expanded by replicating its body multiple times. This reduces loop overhead and can improve instruction-level parallelism. Again, aliasing concerns can prevent loop unrolling, but restrict removes those concerns.

Consider this example:

void add_arrays(int restrict a, int restrict b, int restrict c, int n) { for (int i = 0; i < n; i++) { c[i] = a[i] + b[i]; } } 

Because a, b, and c are declared restrict, the compiler knows that they do not point to overlapping memory regions. This allows it to vectorize the loop, performing multiple additions in parallel. Without restrict, the compiler would have to assume that a, b, and c might overlap, preventing vectorization. As noted in “High Performance Computing” by Charles Severance [^2^], leveraging restrict in such scenarios can yield order-of-magnitude performance improvements.

Practical Usage and Considerations

While restrict can be a powerful tool, it’s essential to use it judiciously and with a clear understanding of its implications. Incorrect use of restrict can lead to undefined behavior and difficult-to-debug errors. Before using restrict, carefully analyze your code to ensure that the pointers involved truly do not alias.

One common mistake is using restrict when pointers can alias. For example, if you pass the same array as both input and output to a function that uses restrict, you’re violating the contract with the compiler. This will lead to unpredictable results. Always double-check your pointer arithmetic and memory management to ensure that your restrict declarations are valid. Static analysis tools can help identify potential aliasing issues, but ultimately, it’s the programmer’s responsibility to ensure correctness.

Here are some best practices for using restrict:

  • Use restrict only when you are absolutely certain that the pointers do not alias.
  • Document your use of restrict clearly in your code.
  • Use static analysis tools to help identify potential aliasing issues.
  • Test your code thoroughly after adding restrict to ensure that it behaves as expected.

Furthermore, be aware that the restrict keyword only applies within the scope in which the pointer is declared. If you pass a restrict pointer to another function, the receiving function does not automatically inherit the restrict qualifier. You must explicitly declare the pointer as restrict in the receiving function as well. Proper application and validation of restrict pointers can provide significant performance boosts while maintaining code integrity, as demonstrated in various numerical computation libraries [^3^].

restrict vs. Other Optimization Techniques

The restrict keyword is just one of many tools available for optimizing C++ code. It’s important to understand how it compares to other techniques and when it’s most appropriate to use. Other common optimization techniques include loop unrolling, vectorization (often achieved through compiler flags or intrinsics), and manual memory management.

Unlike manual loop unrolling or vectorization, restrict doesn’t directly perform any specific optimization. Instead, it enables the compiler to perform optimizations that it would otherwise be hesitant to do. In this sense, restrict is more of an “enabler” than a direct optimization technique. Manual optimization techniques, while potentially more effective in some cases, are also more error-prone and can make code harder to maintain. restrict offers a balance between performance and maintainability by allowing the compiler to do the heavy lifting.

Here’s a comparison:

  • restrict: Enables compiler optimizations by guaranteeing no aliasing. Relatively easy to use but requires careful analysis.
  • Manual Loop Unrolling/Vectorization: Can provide more fine-grained control but is more complex and error-prone.
  • Compiler Flags (e.g., -O3): Can enable various optimizations, but their effects are not always predictable.
  • Memory Management Techniques: Optimizing memory access patterns can significantly improve performance, especially for cache-sensitive algorithms. An example of this would be using cache-friendly data structures.

The best approach to optimization often involves a combination of techniques. Start by profiling your code to identify performance bottlenecks. Then, consider using restrict to enable compiler optimizations where appropriate. Experiment with different compiler flags and, if necessary, explore manual optimization techniques for the most critical sections of code. Remember to always measure the impact of your optimizations to ensure that they are actually improving performance.

Infographic demonstrating the impact of restrict on loop vectorization here.
FAQ About the restrict Keyword ------------------------------
What happens if I violate the restrict contract?
If two restrict pointers alias, the behavior of your program is undefined. This can lead to incorrect results, crashes, or other unpredictable issues.
Is restrict a guarantee or a suggestion to the compiler?
restrict is a contract between the programmer and the compiler. The programmer guarantees exclusive access, and the compiler, in turn, promises to optimize the code more aggressively.
Does restrict always improve performance?
While restrict enables optimizations that can improve performance, it's not a guarantee. The actual impact depends on the specific code and the compiler's ability to take advantage of the restrict qualifier.
Is restrict available in all C++ compilers?
restrict was formally introduced in C99 and later adopted by C++. Most modern C++ compilers support it, but it's always a good idea to check your compiler's documentation.
1. Identify performance-critical sections of your code. 2. Analyze pointer usage to determine if restrict is applicable. 3. Add restrict qualifiers to appropriate pointers. 4. Recompile your code and profile its performance. 5. Verify that the use of restrict has improved performance.

The restrict keyword, while a powerful tool for optimizing C++ code, requires careful consideration and understanding. By promising the compiler that a pointer provides exclusive access to a memory region, you enable it to perform aggressive optimizations like vectorization and loop unrolling. However, violating this promise leads to undefined behavior, making it crucial to use restrict only when you’re absolutely certain that the pointers involved do not alias. Mastering restrict is another step towards becoming a more proficient and effective C++ developer. Consider exploring other optimization techniques like memory alignment and cache optimization to further enhance your code’s performance. Dive deeper into compiler documentation and experiment with different optimization flags to fully unlock the potential of your hardware. The journey to high-performance computing is continuous, and every optimization technique you learn brings you closer to writing truly efficient and powerful applications.

[^1^]: Intel Performance Optimization Guide: [https://www.intel.com/content/www/us/en/developer/tools/optimization-guide.html](https://www.intel.com/content/www/us/en/developer/tools/optimization-guide.html) [^2^]: High Performance Computing by Charles Severance. [^3^]: Numerical Recipes in C++: The Art of Scientific Computing by William H. Press et al. [https://www.cambridge.org/core/books/numerical-recipes-in-c/5454F3F19597634D56827F73B6048757](https://www.cambridge.org/core/books/numerical-recipes-in-c/5454F3F19597634D56827F73B6048757) Question & Answer :
I was always unsure; what does the restrict keyword mean in C++?

Does it mean the two or more pointer given to the function does not overlap? What else does it mean?

As others said, it means nothing as of C++14, so let’s consider the __restrict__ GCC extension which does the same as the C99 restrict.

C99

restrict says that two pointers cannot point to overlapping memory regions. The most common usage is for function arguments.

This restricts how the function can be called, but allows for more compile optimizations.

If the caller does not follow the restrict contract, undefined behavior can occur.

The C99 N1256 draft 6.7.3/7 “Type qualifiers” says:

The intended use of the restrict qualifier (like the register storage class) is to promote optimization, and deleting all instances of the qualifier from all preprocessing translation units composing a conforming program does not change its meaning (i.e., observable behavior).

and 6.7.3.1 “Formal definition of restrict” gives the gory details.

A possible optimization

The Wikipedia example is very illuminating.

It clearly shows how as it allows to save one assembly instruction.

Without restrict:

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

Pseudo assembly:

load R1 ← *x ; Load the value of x pointer load R2 ← *a ; Load the value of a pointer add R2 += R1 ; Perform Addition set R2 → *a ; Update the value of a pointer ; Similarly for b, note that x is loaded twice, ; because x may point to a (a aliased by x) thus ; the value of x will change when the value of a ; changes. load R1 ← *x load R2 ← *b add R2 += R1 set R2 → *b 

With restrict:

void fr(int *restrict a, int *restrict b, int *restrict x); 

Pseudo assembly:

load R1 ← *x load R2 ← *a add R2 += R1 set R2 → *a ; Note that x is not reloaded, ; because the compiler knows it is unchanged ; "load R1 ← *x" is no longer needed. load R2 ← *b add R2 += R1 set R2 → *b 

Does GCC really do it?

g++ 4.8 Linux x86-64:

g++ -g -std=gnu++98 -O0 -c main.cpp objdump -S main.o 

With -O0, they are the same.

With -O3:

void f(int *a, int *b, int *x) { *a += *x; 0: 8b 02 mov (%rdx),%eax 2: 01 07 add %eax,(%rdi) *b += *x; 4: 8b 02 mov (%rdx),%eax 6: 01 06 add %eax,(%rsi) void fr(int *__restrict__ a, int *__restrict__ b, int *__restrict__ x) { *a += *x; 10: 8b 02 mov (%rdx),%eax 12: 01 07 add %eax,(%rdi) *b += *x; 14: 01 06 add %eax,(%rsi) 

For the uninitiated, the calling convention is:

  • rdi = first parameter
  • rsi = second parameter
  • rdx = third parameter

GCC output was even clearer than the wiki article: 4 instructions vs 3 instructions.

Arrays

So far we have single instruction savings, but if pointer represent arrays to be looped over, a common use case, then a bunch of instructions could be saved, as mentioned by supercat and michael.

Consider for example:

void f(char *restrict p1, char *restrict p2, size_t size) { for (size_t i = 0; i < size; i++) { p1[i] = 4; p2[i] = 9; } } 

Because of restrict, a smart compiler (or human), could optimize that to:

memset(p1, 4, size); memset(p2, 9, size); 

Which is potentially much more efficient as it may be assembly optimized on a decent libc implementation (like glibc) Is it better to use std::memcpy() or std::copy() in terms to performance?, possibly with SIMD instructions.

Without, restrict, this optimization could not be done, e.g. consider:

char p1[4]; char *p2 = &p1[1]; f(p1, p2, 3); 

Then for version makes:

p1 == {4, 4, 4, 9} 

while the memset version makes:

p1 == {4, 9, 9, 9} 

Does GCC really do it?

GCC 5.2.1.Linux x86-64 Ubuntu 15.10:

gcc -g -std=c99 -O0 -c main.c objdump -dr main.o 

With -O0, both are the same.

With -O3:

  • with restrict:

    3f0: 48 85 d2 test %rdx,%rdx 3f3: 74 33 je 428 <fr+0x38> 3f5: 55 push %rbp 3f6: 53 push %rbx 3f7: 48 89 f5 mov %rsi,%rbp 3fa: be 04 00 00 00 mov $0x4,%esi 3ff: 48 89 d3 mov %rdx,%rbx 402: 48 83 ec 08 sub $0x8,%rsp 406: e8 00 00 00 00 callq 40b <fr+0x1b> 407: R_X86_64_PC32 memset-0x4 40b: 48 83 c4 08 add $0x8,%rsp 40f: 48 89 da mov %rbx,%rdx 412: 48 89 ef mov %rbp,%rdi 415: 5b pop %rbx 416: 5d pop %rbp 417: be 09 00 00 00 mov $0x9,%esi 41c: e9 00 00 00 00 jmpq 421 <fr+0x31> 41d: R_X86_64_PC32 memset-0x4 421: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 428: f3 c3 repz retq 
    

    Two memset calls as expected.

  • without restrict: no stdlib calls, just a 16 iteration wide loop unrolling which I do not intend to reproduce here :-)

I haven’t had the patience to benchmark them, but I believe that the restrict version will be faster.

Strict aliasing rule

The restrict keyword only affects pointers of compatible types (e.g. two int*) because the strict aliasing rules says that aliasing incompatible types is undefined behavior by default, and so compilers can assume it does not happen and optimize away.

See: What is the strict aliasing rule?

Does it work for references?

According to the GCC docs it does: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Restricted-Pointers.html with syntax:

int &__restrict__ rref 

There is even a version for this of member functions:

void T::fn () __restrict__