C/C++ volatile restrict 用法
作者:互联网
volatile
和restrict
是C和C++中的两个关键字,都用于指示编译器优化。
volatile
volatile的本意是“易变的”,用法和const一样:
volatile int a; // 易变的int变量a
volatile int *p; // 指向易变位置的指针p
这个关键字的用处是什么?考虑下面的代码:
val1 = a;
/*
一段没有使用a值的代码
*/
val2 = a;
默认情况下,聪明的编译器可以注意到两段代码之间没有使用x。所以在第一次赋值时,会将a值放入寄存器,并在第二次赋值的时候直接从寄存器读取值,不用返回内存或cache,提高效率。
但是,如果你的程序是并发程序,或者在中间的代码里使用一些“意想不到的方式”改变了a的值(比如内联汇编),编译器却不知道,程序就会得出错误的结果。这时就需要给a加上volatile,语义即为“这个变量可能在中途在你看不到会被改变”,这样一来,编译器就不会做优化,得到正确结果。
volatile和const看上去是矛盾的,但是他们可以一起用。同样的道理,const只是表示“我这里不能赋值更改”,但其他地方仍可以修改它。
volatile const int a;
restrict
restrict用于修饰一个指针,表示“只能通过这个指针访问其变量”:
int ar[10];
int restrict *p1 = (int*)malloc(10*4);
int *p2 = ar;
p1指向的内存,肯定是只能通过p1访问的,所以可以添加restrict;但p2所指的内存,通过ar
和p2
都可以访问,所以不应添加restrict
。另外,restrict
不是标准,算是方言,所以不是所有地方都有,形式也不一定相同(也可能是__restrict
等等)。
如果编译器知道restrict信息,就可以更好的优化,比如:
for (i=0; i<10; i++) {
p1[i] += 5;
p2[i] += 5;
ar[i] *= 2;
p2[i] += 5;
p1[i] += 5;
}
编译器知道p1[i]是独立的,所以它可以把两条p1有关的语句合成一条:
p1[i] += 10
。
而p2则显然不能替换,因为p2和ar是同一个值,替换了结果就错了。与votatile相反,默认情况下,编译器假设的是“最坏的情况”,不做任何优化。只有手动添加restrict后,才会认为变量唯一,可以选择捷径。
另一个典型例子是用于形参指针:
int f(int *p1, int *p2) {
*p1 = 10;
*p2 = 12;
return *p1;
}
这段函数的结构很简单,编译器能推断优化,直接返回10吗?不可以,因为如果p1和p2相等,返回值就是12。所以这个优化不能做;而如果你明知传入的p1和p2一定不相等,你就可以在形参列表里加上限制,如此一来,编译器就知道p1和p2一定指向两个变量,大胆的优化。
int f(restrict int *p1, restrict int *p2) {
*p1 = 10;
*p2 = 12;
return *p1; // 大胆优化为10
}
另一个经典例子是memcpy和memmove函数的声明:
void * memcpy(void * restrict s1, const void * restrict s2, size_t n);
void * memmove(void * s1, const void * s2, size_t n);
这两个函数都从位置s2把n字节拷贝到位置s1。memcpy()
函数要求两个位置不重叠,但是memove()
没有这样的要求。所以第一个函数声明为restrict说明两个指针是访问到相应数据的唯一方式,而第二个没有,他允许拷贝的区间有重叠。
当你使用了restrict,就等于说明“只能通过这个指针访问其区域”,编译器会按照这个方式优化,你仍然可以调用相同的重叠参数传入,但会得到错误的结果(比如上面的f)。
标签:p2,p1,int,C++,restrict,编译器,volatile 来源: https://www.cnblogs.com/ofnoname/p/16513837.html