restrict关键字
值得注意的是,一旦你决定使用restrict来修饰指针,你必须得保证它们之间不会互相重叠,编译器不会替你检查。
关键字restrict有两个读者。一个是编译器,它告诉编译器可以自由地做一些有关优化的假定。另一个读者是用户,他告诉用户仅使用满足restrict要求的参数。一般,编译器无法检查您是否遵循了这一限制,如果您蔑视它也就是在让自己冒险。
使用restrict的好处是,能帮助编译器进行更好的优化代码,生成更有效率的汇编代码
In the C programming language, as of the C99 standard, restrict is a keyword that can be used in pointer declarations. The restrict keyword is a declaration of intent given by the programmer to the compiler. It says that for the lifetime of the pointer, only it or a value directly derived from it (such as pointer + 1) will be used to access the object to which it points. This limits the effects of pointer aliasing, aiding caching optimizations. If the declaration of intent is not followed and the object is accessed by an independent pointer, this will result in undefined behavior.
Optimization优化
If the compiler knows that there is only one pointer to a memory block, it can produce better code. For instance:
void updatePtrs(size_t *ptrA, size_t *ptrB, size_t *val)
{
*ptrA += *val;
*ptrB += *val;
}
In the above code, the pointers ptrA, ptrB, and val might refer to the same memory location, so the compiler will generate less optimal code :
load R1 ← *val ; Load the value of val pointer
load R2 ← *ptrA ; Load the value of ptrA pointer
add R2 += R1 ; Perform Addition
set R2 → *ptrA ; Update the value of ptrA pointer
; Similarly for ptrB, note that val is loaded twice,
; because ptrA may be equal to val.
load R1 ← *val
load R2 ← *ptrB
add R2 += R1
set R2 → *ptrB
However if the restrict keyword is used and the above function is declared as :
void updatePtrs(size_t *restrict ptrA, size_t *restrict ptrB, size_t *restrict val);
then the compiler is allowed to assume that ptrA, ptrB, and val point to different locations and updating one pointer will not affect the other pointers. The programmer, not the compiler, is responsible for ensuring that the pointers do not point to identical locations.
Now the compiler can generate better code as follows:
load R1 ← *val
load R2 ← *ptrA
add R2 += R1
set R2 → *ptrA
; Note that val is not reloaded,
; because the compiler knows it is unchanged
load R2 ← *ptrB
add R2 += R1
set R2 → *ptrB
Note that the above assembly code is shorter because val is loaded once.
例子1
C库中有两个函数可以从一个位置把字节复制到另一个位置。在C99标准下,它们的原型如下:
void * memcpy(void * restrict s1, const void * restrict s2, size_t n);
void * memove(void * s1, const void * s2, size_t n);
这两个函数均从s2指向的位置复制n字节数据到s1指向的位置,且均返回s1的值。两者之间的差别由关键字restrict造成,即memcpy()可以假定两个内存区域没有重叠。memmove()函数则不做这个假定,因此,复制过程类似于首先将所有字节复制到一个临时缓冲区,然后再复制到最终目的地。如果两个区域存在重叠时使用memcpy()会怎样?其行为是不可预知的,既可以正常工作,也可能失败。因此,使用memcpy()时,您必须确保没有重叠区域。这是程序员的任务的一部分。
例子2
#include<stdio.h>
int main(void)
{
int a = 5;
int * restrict ptr = &a;
++a;
*ptr += 8;
int *p = &a;
++(*p);
printf("%d\n", a);
return 0;
}
编译命令 gcc -std=c99 test.c // 编译和运行都OK,but never这样干,这就违背了// restrict的用意。它主要用来修饰函数指针参数和由malloc得到的指针。
restrict是C99中新增的关键字,在C89和C++中都不支持,在gcc中可以通过-std=c99来得到对它的支持。