C - Restrict Keyword in C
The restrict keyword in C is a type qualifier introduced in the C99 standard. It is mainly used with pointers to tell the compiler that, for a particular period of execution, a pointer is the primary way through which an object is accessed. This information allows the compiler to perform certain optimizations because it can assume that other pointers are not being used to access the same object in conflicting ways.
1. What is the restrict Keyword?
Normally, when a function receives multiple pointers, the compiler cannot always determine whether those pointers refer to the same memory location.
For example:
void add(int *a, int *b, int *c)
{
*c = *a + *b;
}
Here, a, b, and c could potentially point to the same or overlapping memory locations. Therefore, the compiler has to be careful when optimizing the function.
With restrict, we can provide additional information:
void add(int *restrict a, int *restrict b, int *restrict c)
{
*c = *a + *b;
}
This tells the compiler that the objects accessed through these restricted pointers are not being accessed through conflicting pointer expressions during the relevant execution.
2. Why is restrict Used?
The primary purpose of restrict is optimization.
Consider a function that processes two arrays:
void add_arrays(int *restrict a, int *restrict b,
int *restrict result, int n)
{
for (int i = 0; i < n; i++)
{
result[i] = a[i] + b[i];
}
}
Without restrict, the compiler must consider the possibility that result overlaps with a or b.
For example:
add_arrays(a, b, a, 10);
If the arrays overlap, writing to result could change values that the function still needs to read from a.
When restrict is used correctly, the programmer is promising that such conflicting access does not occur. The compiler can therefore make stronger assumptions and potentially generate more efficient machine code.
3. Basic Syntax
The general syntax is:
type *restrict pointer;
For example:
int *restrict ptr;
A function parameter can also use it:
void process(int *restrict data);
Multiple restricted pointers can be declared:
void copy_data(int *restrict destination,
const int *restrict source,
int n);
This is commonly useful for functions that operate on separate memory regions.
4. Example Without restrict
Consider:
void calculate(int *a, int *b)
{
*a = 10;
*b = 20;
printf("%d\n", *a);
}
The compiler cannot simply assume that a and b point to different objects.
They could be called like this:
int x;
calculate(&x, &x);
In this case, both pointers refer to the same object.
After:
*a = 10;
*b = 20;
the value of x becomes 20, so the final printf produces:
20
The compiler therefore has to account for possible aliasing.
5. Example With restrict
Now consider:
void calculate(int *restrict a, int *restrict b)
{
*a = 10;
*b = 20;
printf("%d\n", *a);
}
The use of restrict communicates that the programmer does not intend a and b to be used to access the same object in this conflicting manner.
Therefore, calling:
int x;
calculate(&x, &x);
violates the requirements associated with the restrict contract and results in undefined behavior.
This is an important point: restrict is not a command that prevents pointers from pointing to the same address. Instead, it establishes an assumption that the programmer promises to obey. If that assumption is violated, the program can have undefined behavior.
6. Understanding Pointer Aliasing
To understand restrict, it is useful to understand aliasing.
Aliasing occurs when two or more pointers refer to the same memory location.
For example:
int value = 50;
int *p = &value;
int *q = &value;
Here:
p ──┐
├──> value
q ──┘
Both p and q access the same object.
The compiler must consider such possibilities when optimizing code.
When restrict is used appropriately, it gives the compiler additional information about these memory accesses.
7. restrict With Arrays
restrict is particularly useful when working with arrays.
Consider:
void multiply(int *restrict a,
const int *restrict b,
int n)
{
for (int i = 0; i < n; i++)
{
a[i] = b[i] * 2;
}
}
The function expects a and b to represent separate regions of memory.
A valid call could be:
int a[5];
int b[5];
multiply(a, b, 5);
Here, a and b are separate arrays.
This allows the compiler to reason more effectively about memory accesses and may enable optimizations such as improved loop processing.
8. restrict Does Not Make Data Constant
A common misunderstanding is that restrict prevents modification.
It does not.
For example:
int *restrict ptr;
does not mean that the value pointed to by ptr is read-only.
You can still write:
*ptr = 100;
The keyword const is used when the pointed-to data should not be modified through that pointer:
const int *ptr;
These keywords have different purposes.
const int *ptr;
means the data should not be modified through ptr.
int *restrict ptr;
provides an aliasing-related assumption for optimization.
They can also be combined:
const int *restrict ptr;
This means the data is not modified through that pointer, while restrict provides the relevant aliasing promise.
9. restrict and Function Parameters
One of the most common applications of restrict is in function parameters.
For example:
void process(int *restrict data, int size)
{
for (int i = 0; i < size; i++)
{
data[i] *= 2;
}
}
The function receives a pointer to an array and modifies its elements.
More importantly, restrict becomes useful when several pointers are involved:
void combine(int *restrict result,
const int *restrict first,
const int *restrict second,
int n)
{
for (int i = 0; i < n; i++)
{
result[i] = first[i] + second[i];
}
}
The programmer is indicating that these memory regions should not overlap in a way that violates the restrict requirements.
10. restrict and Compiler Optimization
Modern compilers perform many optimizations automatically.
Suppose a loop repeatedly reads from one array and writes to another:
void process(int *a, int *b, int n)
{
for (int i = 0; i < n; i++)
{
a[i] = b[i] + 1;
}
}
Without additional information, the compiler has to consider whether a and b overlap.
With:
void process(int *restrict a,
const int *restrict b,
int n)
{
for (int i = 0; i < n; i++)
{
a[i] = b[i] + 1;
}
}
the compiler has stronger information about the intended memory relationships.
Depending on the compiler, optimization settings, processor, and surrounding code, this can help with optimizations such as vectorization or reducing unnecessary memory loads.
However, restrict does not automatically make every program faster. Its usefulness depends on the code and compiler.
11. restrict Is a Programmer's Contract
The most important concept to remember is that restrict creates a contract between the programmer and the compiler.
Consider:
void update(int *restrict a, int *restrict b)
{
*a = 10;
*b = 20;
}
The programmer is promising that the way these pointers are used satisfies the requirements of restrict.
If the caller provides overlapping objects in a way that violates those requirements, the behavior is undefined.
Therefore, restrict should not be added simply because it appears to make code faster.
It should be used when the programmer knows the memory-access relationship and can guarantee that the requirements are satisfied.
12. Difference Between const and restrict
The following comparison is useful:
| Keyword | Main Purpose |
|---|---|
const |
Prevents modification through a particular access path |
restrict |
Provides an aliasing assumption that can enable optimization |
volatile |
Tells the compiler that accesses to an object must not be optimized away or freely combined because its value may change outside ordinary program flow |
For example:
const int *ptr;
means the program should not modify the integer through ptr.
Whereas:
int *restrict ptr;
concerns how the object is accessed through pointers.
13. Practical Example
A useful real-world example is an array-processing function:
#include <stdio.h>
void add_arrays(int *restrict result,
const int *restrict a,
const int *restrict b,
int n)
{
for (int i = 0; i < n; i++)
{
result[i] = a[i] + b[i];
}
}
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int b[5] = {10, 20, 30, 40, 50};
int result[5];
add_arrays(result, a, b, 5);
for (int i = 0; i < 5; i++)
{
printf("%d ", result[i]);
}
return 0;
}
Output:
11 22 33 44 55
Here, result, a, and b are separate arrays. The restrict qualifiers communicate this intended non-overlapping access relationship to the compiler.
14. Advantages of restrict
The major advantages include:
-
It provides useful information to the compiler about pointer aliasing.
-
It can enable more aggressive compiler optimizations.
-
It can improve the potential for loop vectorization.
-
It is useful in performance-sensitive numerical and array-processing programs.
-
It makes the programmer's intended memory-access relationship explicit.
15. Limitations and Precautions
restrict should be used carefully.
It does not:
-
Prevent two pointers from having the same address.
-
Automatically make a program faster.
-
Replace proper memory management.
-
Protect memory from modification.
-
Perform runtime checking of pointer relationships.
-
Make pointers safer.
Incorrect use can introduce undefined behavior because the compiler is allowed to rely on the promises associated with restrict.
16. Key Points to Remember
The restrict keyword is mainly concerned with pointer aliasing and compiler optimization. It tells the compiler that, under the rules of the restrict qualifier, a particular pointer is being used as the relevant access path to an object. This can allow the compiler to optimize code more aggressively.
A simple way to remember it is:
const → protects against modification through a pointer
restrict → provides an aliasing promise
volatile → preserves required observable memory accesses
restrict is especially important in high-performance C programs that process arrays, buffers, numerical data, and other large blocks of memory. Its benefits depend on correct usage, so it should only be applied when the programmer can guarantee that the required aliasing conditions are satisfied.