4.6.限制¶
4.6.1.概述¶
restrict
关键字是指针变量类型的限定符。通过对指针 p 的类型声明应用限制,编程器会向编译器做出以下保证:
在p的声明范围内,只有p或基于p的表达式将用于访问p 指向的对象。
编译器可以利用这种保证来生成更高效的代码。
保证说明:
在 p 的声明范围内
p 是一个指针变量。例如
p1
、s.p2
、p3[i]
以及p4
和p5
(在p4->p5[]
中)。限制适用的程序区域是 p 声明的范围。只有 p 或基于 p 的表达式
在
*p
、p[i]
和p[i+3]
之类的访问中,这称为指针。将用于访问 p 指向的对象
只有实际的获取和存储才是访问。
p[i]
是访问,但&p[i]
和p+i
不是。
警告
错误使用 restrict
会导致编译器生成不正确的代码。错误使用的一个例子是对指向存储器中重叠对象的指针应用 restrict
。有关示例,请参阅错误用法。
4.6.2.示例¶
下面的比较说明了使用 restrict
的有效性。向 a1
和 b1
指针的类型添加 restrict
限定符会向编译器保证这些指针不会用于访问与 t->sum1
或 t->sum2
相同的存储器位置。这使编译器能够为循环生成更有效的指令序列。
在表 4.7 中,循环执行了 256 次。周期数是在 F280049C 上使用 RAM 中的代码和数据以及 -O3--opt_for_speed=5
测得的。使用 restrict
,周期数从 3618 减少至 1209 次。
#include<stdint.h>typedefstruct{float*a;float*b;floatsum1;floatsum2;int16_tN;}Test; voidfoo2(Test*t){float*a1=t->a;float*b1=t->b;inti;for(i=0;i<t->N;i++){t->sum1+=a1[i]*b1[i];t->sum2+=a1[i]*a1[i];}} |
#include<stdint.h>typedefstruct{float*a;float*b;floatsum1;floatsum2;int16_tN;}Test; voidfoo1(Test*t){float*restricta1=t->a;float*restrictb1=t->b;inti;for(i=0;i<t->N;i++){t->sum1+=a1[i]*b1[i];t->sum2+=a1[i]*a1[i];}} |
3618 个周期 | 1209 个周期 |
注释
restrict
仅在 --opt_level=2
或更高级别有效。
4.6.3.使用¶
4.6.3.1.全局变量¶
int*restrictp1;int*restrictp2;externintA[];
总之,这些全局变量的文件范围声明会向编译器保证,如果使用 p1、p2 或 A[] 中的任何一个访问对象,则不会使用其他任何变量访问该对象。此外,文件范围包含所有其他范围,因此通过局部指针变量的访问无法访问 p1 或 p2 指向的对象。
4.6.3.2.函数参数¶
函数声明中的参数具有函数原型范围,它在声明的末尾终止:
voidfoo(float*restrictv1,float*v2,intn);
在此函数的定义中,这些参数与 i 具有相同的块范围:
voidfoo(float*restrictv1,float*v2,intn){inti;...}
限制 v1
会向编译器保证,v1
指向的对象不会与 foo() 主体中其他指针指向的对象重叠。
注释
数组在 C 中通过引用传递。若要对数组参数进行限定,restrict 关键字应如下所示:
voidfoo(shorta[restrict100]);
4.6.3.3.局部指针变量¶
voidfoo(Test*t){float*restricta1=t->a;float*restrictb1=t->b;...}
在局部变量 a1
和 b1
中为指针的类型添加 restrict 限定,使编程器能够将通过指针进行的访问的性质限制在函数的较小范围内。