volatile与restrict

 

一、类型限定词volatile
限定词volatile告诉编译器该变量除了可以被程序改变以外还可被其他代理改变。
它通常被用于硬件地址和与其它并行运行的程序共享的数据。
如:

volatile int locl
volatile int *ploc;

你可能会奇怪为什么ANSI觉得有必要把volatile作为一个关键字。
原因是它可以方便编译器优化。
例如

val1 = x;
/* 一些不使用x的代码 */
val2 = x;

一个聪明的编译器可能注意到你使用了两次x,而没有改变它的值。那么,它有可能把x临时存储在一个寄存器中。
接着,当val2需要x时,可以通过从寄存器而非初始的内存位置中读取该值以节省时间。
这个过程被称为缓存(caching)。
通常,缓存是一个好的优化方式,但是如果在两个语句间其他代理改变了x的话就不是这样了。
如果没有规定volatile关键字,那么编译器将无从得知这种改变是否可能发生。
在ANSI中,如果声明中没有volatile关键字,那么编译器就可以假定一个值在使用过程中没有被修改,它就可以试着优

化代码。
一个值可以同时是const 和 volatile。
例如,硬件时钟一般设定为不能由程序改变,这一点使它成为const;
但它被程序以外的代理改变,这使它成为volatile.
那么我们可以这么声明:

volatile const int loc;
const volatile int *ploc;

二、类型限定词restrict
关键字restrict用来消除数据间的相关性,编译器从而可以安排语句的并行执行。
它只可以用于指针,并表明指针是访问一个数据对象的唯一且初始的方式。
我们通过一个例子来看看:

int ar[10];
int * par = ar;
int * restrict restar = (int *)malloc (10 * sizeof (int));

注意,指针restar是访问由malloc ()分配的内存的唯一且初始的方式。
因此,它可以由关键字restrict限定。
而指针par既不是初始的,也不是访问数组ar中数据的唯一方式,因此不可以把它限定为restrict。
考虑下面的语句:

for (n = 0n < 10n++)
{
    
par[n] += 5;
    
restar[n] += 5;
    
ar[n] *= 2;
    
par[n] += 3;
    
restar[n] += 3;
}

知道了restar是放问它所指向数据块的唯一初始化方式,编译器就可以用具有同样效果的一条语句来代替包含restar的

两个语句。

resatr [n] += 8;

然而,编译器将两个包含par的语句精简为一个语句将导致计算错误。

par[n] +=8;

原因是ar[n] *= 2;这条语句在par[n] += 3之前已经改变了par指针所指向数据的值。

restrict的作用:帮助编译器确定使指针进行数值计算时,是否可以进行优化。
可以将关键字restrict作为指针型函数参量的限定词使用。
这意味着编译器可以假定在函数体内没有其它标识符修改指针指向的数据,因而可以试着优化代码,反之则不然。

voie * memcpy (void * restrict s1const void * restrict s2size_t n);

关键字restrict有两个读者。
一个是编译器,它告诉编译器可以自由地去做一些有关优化的假定。
一个是用于,它告诉用户仅使用满足restrict要求的参数。

1).volatile

   影响编译器编译的结果,指出,volatile 变量是随时可能发生变化的,每次使用时都需要去内存里重新读取它的值,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++ 在产生release版可执行码时会进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化。)。 例如:
   volatile int i=10;
   int j = i;
   ...
   int k = i;
   volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。 而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样一来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。
   建议使用volatile变量的场所:
   (1) 并行设备的硬件寄存器
   (2) 一个中断服务子程序中会访问到的非自动变量(全局变量)
   (3) 多线程应用中被几个任务共享的变量
2).volatile和__volatile__:
 a. volatile是C语言定义的关键字,gcc为了需要又定义了__volatile__,它和
    volatile表达的是同一意思。
 b. volatile的本意是"易变的",由于访问寄存器的速度快于访存,所以编译器一般
    都会作优化以减少访存。如果变量加上volatile修饰,则编译器就不会对此变量
    的读写操作进行优化,即不通过寄存器缓冲而直接访存。
 c. __asm__ __volatile__一起指示编译器不要改动优化后面的汇编语句。
3).sig_atomic_t:
   当把变量声明为该类型是,则会保证该变量在使用或赋值时, 无论是在32位还是64位的机器上都能保证操作是原子的, 它会根据机器的类型自动适应。
   今天看源代码时,看到sig_atomic_t这个类型,平时用得较少,平时一般是用int类型来代替。
   这个类型是定义在signal.h文件中。下面来说说这个类型。
   在处理信号(signal)的时候,有时对于一些变量的访问希望不会被中断,无论是硬件中 断还是软件中断,这就要求访问或改变这些变量需要在计算机的一条指令内完成。通常情况下,int类型的变量通常是原子访问的,也可以认为 sig_atomic_t就是int类型的数据,因为对这些变量要求一条指令完成,所以sig_atomic_t不可能是结构体,只会是数字类型。
   在linux里这样定义:
   typedef int __sig_atomic_t;
   另外gnu c的文档也说比int短的类型通常也是具有原子性的,例如short类型。同时,指针(地址)类型也一定是原子性的。 该类型在所有gnu c库支持的系统和支持posix的系统中都有定义。
原文地址:https://www.cnblogs.com/ghost240/p/2526708.html