编程语言
首页 > 编程语言> > C/C++ volatile restrict 用法

C/C++ volatile restrict 用法

作者:互联网

volatilerestrict是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所指的内存,通过arp2都可以访问,所以不应添加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