想把学习过程中的一些心得记录下来,权当自己学习经历中的笔记吧。如果你无意中看到这些文章,能帮我指出其中一些理解不正确的地方,在这里小弟将万分感谢。呵呵。
volatile和restrict这两个类型限定词的运用与编译器的优化存在着一定的关系。volatile这个关键字用在变量类型定义上,指明这个变量的值存在不确定因素。也就是说这个变量不光会被我们编写的程序改变值,也可能会被某个外部代理改变(比如:某个硬件中断、外部程序等)。这样就不能保证如果程序没有改变这个变量值,而又存在多次调用后进入寄存器中的值就一定正确。
从编译器的优化角度,举个例子:
int x=5;
int a, b;
a = x;
b = x;
由于程序没有更改X的值,但又存在多次调用,编译器为了优化运行速度,会给a赋值后,把X的值5从内存放入到寄存器中。当给b赋值时,不是再次读取X内存地址中的值,而是直接把寄存器中的5赋给b。这一优化对于普通变量没有问题。但如果定义成 volatile int x;则表明x可以被程序代码外的其他代理改变值。如果编译器也采用这样的优化,很可能在给b赋值时,x的值已经被程序外部的某个硬件中断改变了。这样从寄存器获取到的值肯定是不正确的。
因此当给变量加上volatile关键字,除了表示这一变量可以被其他代理改变值,也明确说明编译器不能为此变量进行上面那种方式的优化:每次调用这一变量,都从变量的地址中获取值,而不是寄存器(此变量使用的硬件内存地址是与其他并行运行的程序共享数据的,因此不管是程序自身改变变量值,还是其他代理改变变量值,都是改变内存地址中的数据)。
看个有趣的例子:
int square(volatile int *a)
{
return (*a * *a);
}
函数的目的本来是计算平方根,但由于a指针用了volatile关键字,两次获取a指针地址中的值不能完全保证一样,所以计算出来的结果也未必就是我们需要的。考虑修改成这样:
int square(volatile int *a)
{
int temp = *a;
return (temp * temp);
}
restrict关键字只能用来修饰指针,表示被定义的指针是访问指针中数据的唯一途径。这一目的是告诉编译器可以进行一些优化。看个例子:
int x = 2;
int *a = (int *) malloc(sizeof(int));
*a = 2;
int *b = &x;
*a += 2;
*b += 2;
x *= 3;
*a += 3;
*b += 3;
编译器进行优化时可以用一条语句代替:*a += 5;这对于a来说是正确的,但如果用*b += 5来优化b是不正确的。因为其他变量影响了结果。因此,当编译器不确定某些因素时,会放弃寻找某个途径进行优化。如果在变量前加上restrict关键字。则告诉编译器可以“放心大胆”的进行优化。但编译器并不会验证你定义为restrict的指针,是否真正是某个数据的唯一访问途径;就像数组的下标越界一样,如果你不遵守规则,编译器并不会指出错误,但后果由你自己负责:)
同样看个有趣的类子:
void change_array(restrict int *array, const restrict int *value,const int size)
{
for(int i=0;i
{
array[i] += *value;
}
}