参考自restrict

restrict解释

restrict关键字出现于C99标准,wiki上的解释restrict from wiki

实例

上面这堆翻译好像我只看懂了:restrict关键字用于指针的声明。那到底什么意思呢?在进一步了解restrict之前,需要了解什么是pointer aliasing

pointer aliasing是指多个指针指向同一块内存区域,例如:

int a = 10;
int* ptr1 = &a;
int* ptr2 = &a;

则ptr1、ptr2互称对方为自己的pointer aliasing。考虑下面这个函数:

int foo(int* a, int* b)
{
*a = 2;
*b = 3;
return *a + *b;
}

一般情况下,编译器翻译出来的汇编代码会类似(以ARM汇编为例):

01    str     r0, [fp, #-8]			@传递参数指针a
02 str r1, [fp, #-12] @传递参数指针b
03 ldr r3, [fp, #-8] @得到指针a
04 mov r2, #2
05 str r2, [r3] @*a = 2;
06 ldr r3, [fp, #-12] @得到指针b
07 mov r2, #3
08 str r2, [r3] @*b = 3;
09 ldr r3, [fp, #-8] @得到指针a
10 ldr r2, [r3] @r2 = *a, 重读*a,因为如果a==b,则08行会覆盖05行的赋值!!!!!
11 ldr r3, [fp, #-12] @得到指针b
12 ldr r3, [r3] @r3 = *b;
13 add r0, r2, r3 @*a + *b;

如果我们可以提前告诉编译器a != b,则编译器可以优化出更简洁的汇编(假设a、b指针都是非volatile的):

01    str     r0, [fp, #-8]			@传递参数指针a
02 str r1, [fp, #-12] @传递参数指针b
03 ldr r3, [fp, #-8] @得到指针a
04 mov r2, #2
05 str r2, [r3] @*a = 2;
06 ldr r3, [fp, #-12] @得到指针b
07 mov r1, #3
08 str r1, [r3] @*b = 3;
09 add r0, r1, r2 @*a + *b;

比上面少了4条指令。为什么呢?因为上面那段汇编中得考虑一种情况,如果指针a == b,那么第08行的指针赋值会覆盖05行的指针赋值,为了保险起见,*a + *b就得重新从内存中取值,才能得到正确的结果。如果我们显式的告诉编译器a != b,则编译器就会使用寄存器里面的缓存数据来优化代码。如何显式告诉编译器呢?就是用restrict关键字来修饰指针。

注意事项

但是有点需要注意,不是说用restrict修饰指针后就保证了指针不会出现pointer aliasing,这种保证由程序员自己编码时确保。就是说程序员自己保证对同一内存区域的引用只有一个指针指向,然后通过restrict修饰此指针变量,在开启编译优化后,编译器将会特别留意被restrict修饰的指针,进行优化。

restrict关键字只能修饰object类型的指针(例如int restrict *pfloat (* restrict foo)(void)都是错误的用法)。restrict仅用于修饰左值(lvalue)。例如使用它强制转换指针或者修饰函数返回值都是错误的用法。

解释下2个名词:

object:

在C语言中,对象是执行环境中的数据存储区域,其内容可以表示值(当解释为具有特定类型时,值是对象内容的含义)。每个对象有如下特征:

  • 尺寸(能用sizeof计算)
  • 有对齐需求
  • 存储期(automatic, static, allocated, thread-local)
  • 有效类型
  • 值(可能是不确定的)
  • 可选地,表示此对象的标识符(就是变量名)

int restrict *p 修饰的不是指针。

float (* restrict foo)(void),foo指针指向的区域明显不具备存储数据的能力,*foo = 12是非法的,因为如果它是一个函数指针,指向的内存区域是只读的。

左值(lvalue):

只能出现在C语言赋值表达式左边的。

显然ret = fun()、a = (int)b就说明了函数返回值,强转都是属于右值。

04-22 23:09