C99中的restrict和C89的volatilekeyword

1、restrict

它仅仅能够用于限定指针。告知编译器该指针是訪问一个数据对象的唯一且初始的方式。即不存在其他进行改动操作的途径。

主要作用是能够让编译器进行一些优化,生成更高效的目标代码。

看个样例:

int foo(int *a,int *b)
{
	*a = 1;
	*b = 2;
	return *a;
}

int main()
{
	int *p, *q, ret;

	ret = foo(p, q);

	return 0;
}

我们用gcc -O2 -std=c99选项进行编译。foo()的反汇编例如以下图:

我们把foo()改为:

int foo(int *restrict a,int *restrict b)
{
	*a = 1;
	*b = 2;
	return *a;
}

依旧O2编译:

能够清楚地看到,编译器对返回值做了优化处理.

之所曾经一个样例。返回值要从内存中获取,是由于编译器不确定指针a是不是唯一的一个能够訪问到那片内存的指针。又可能还有其它指针訪问或改动了那片内存。因此,即使是O2优化,返回值还是从内存中得来的。编译器是要保证百分之百的正确。

后面一个样例。明白告知了编译器,a是唯一訪问到a所指向的内存的指针,所以编译器能够放心大胆地直接向寄存器写返回值。

然而,标准里还有这种话:

If the declaration of intent is not followed and the object is accessed by an independent pointer, this will result in
undefined behavior

我们把main函数改成这样:

int foo(int *restrict a,int *restrict b)
{
	*a = 1;
	*b = 2;
	return *a;
}

int main()
{
	int *p, *q, ret;

	ret = foo(p, p);

	return 0;
}

这样,依照本意,ret的值应该为2才对,然而:

程序得到了错误的结果。

因此,正如李林老师在《Linux环境高级编程》里指出:restrict的限制(仅仅能通过该指针訪问),是由程序猿来保证的,编译器并不能全然保证。[1]

再如:

int foo(int *restrict a,int *b)
{
	*a = 1;
	*b = 2;
	return *a;
}

在我的机器上反汇编,发现O1未做处理。但O2时做了优化处理。

未做优化的原因。事实上是编译器对指针b的顾虑:万一b也指向的空间和a一样呢?

所以,优化需慎重

2、volatile

与restrict让编译器优化相反。volatile是阻止编译器优化。

简单地说,volatile告诉编译器该被变量除了可被程序改动外,还可能被其它代理、线程改动。

因此,当使用volatile 声明的变量的值的时候,系统总是又一次从它所在的内存读取数据,而不使用寄存器中的缓存的值。

继续看个样例:

static int flag;

void foo()
{
	flag = 0;
	while (flag < 2)
		;
}

int main()
{
	foo();
	return 0;
}

依旧用-O2选项:

注意圈出来的那一行,这是一个死循环,也就是说,flag < 2 这个条件被编译器觉得是永真!所以还是这句话,优化需慎重。

而加上keyword之后:

static volatile int flag;

注意圈出来的那一行:flag的获取是从内存中来的。

这个样例是为了说明volatile能阻止编译器做常量合并、常量传播等优化。

其它的方面的作用能够參考 [2]

一般说来。volatile用在例如以下的几个地方:

1、中断服务程序中改动的供其他程序检測的变量须要加volatile;

2、多任务环境下各任务间共享的标志应该加volatile。

3、存储器映射的硬件寄存器通常也要加volatile说明,由于每次对它的读写都可能由不允许义;

然而矛盾的是:依据相关的标准(C,C++,POSIX,WIN32)和眼下绝大多数实现。对volatile变量的操作并非原子的,也不能用来为线程建立严格的happens-before关系。[3]

參考资料:

[1]:李林《linux程序设计实践》

[2]:具体解释volatile

[3]:volatile wiki

时间: 2024-10-01 06:48:42

C99中的restrict和C89的volatilekeyword的相关文章

C99中的restrict和C89的volatile关键字

1.restrict 它只可以用于限定指针,告知编译器该指针是访问一个数据对象的唯一且初始的方式.即不存在其它进行修改操作的途径. 主要作用是可以让编译器进行一些优化,生成更高效的目标代码. 看个例子: int foo(int *a,int *b) { *a = 1; *b = 2; return *a; } int main() { int *p, *q, ret; ret = foo(p, q); return 0; } 我们用gcc -O2 -std=c99选项进行编译,foo()的反汇编

c89和c99中/运算符和%运算符为负数时的区别

运算式 -8 / 5 = -1.6,在C89中取值为 -1 或 -2,C99的出现,CPU对除法的结果向零取整,上述运算式结果为 -1. 在C89和C99中都要确保 (a / b) * b + a % b = a 已知在C89中 -8 / 5 运算结果为 -1 或 -2,根据公式,-8 % 5 运算结果为 -3 或 2 在C99中 -8 / 5 结果位 -1,那么 -8 % 5 j结果为-3.

C99中的布尔值

长期缺乏布尔类型的问题在C99中得到了解决, C99提供了_Bool型,所以布尔类型的值可以声明为 _Bool flag; _Bool类型的值分为0和!0,一般来说,它只能赋值为0和1. 除了_Bool类型的定义,C99还提供了一个新的头<stdbool.h>,该头提供了bool宏用来代表_Bool,如果程序中包含了<stdbool.h>,布尔类型可以定义为 bool flag; <stdbool.h>头还提供了true和false两个宏,分别代表1和0 flag =

c99标准的restrict关键字

参考自restrict restrict关键字出现于C99标准,wiki上的解释restrict from wiki. In the C programming language, as of the C99 standard, restrict is a keyword that can be used in pointer declarations. The restrict keyword is a declaration of intent given by the programmer

C99 中 main 函数的写法

今天在论坛看见有人讨论 C 语言中 Main 函数的写法,看到结论才知道 Main 函数的正确写法. 被老谭酸菜坑了这么多年,还是记录下吧,或许以后某天不搞 .net,回去折腾 C 语言了. 写法1: 1 int main(void) 2 { 3 // TODO 4 return 0; 5 } 写法2: 1 int main(int argc, char *argv[]) 2 { 3 // TODO 4 return 0; 5 } 两种写法都是符合 C99 标准的,当然第二种是适用范围更广泛.

C语言restrict关键字的使用----可以用来优化代码

C99中新增加了restrict修饰的指针:由restrict修饰的指针是最初唯一对指针所指向的对象进行存取的方法,仅当第二个指针基于第一个时,才能对对象进行存取.对对象的存取都限定于基于由restrict修饰的指针表达式中. 由restrict修饰的指针主要用于函数形参,或指向由malloc()分配的内存空间.restrict数据类型不改变程序的语义.编译器能通过作出restrict修饰的指针是存取对象的唯一方法的假设,更好地优化某些类型的例程. [典型例子] memcpy()在C99中,re

C89和C99区别--简单总结

(1)对数组的增强 可变长数组 C99中,程序员声明数组时,数组的维数可以由任一有效的整型表达式确定,包括只在运行时才能确定其值的表达式,这类数组就叫做可变长数组,但是只有局部数组才可以是变长的.可变长数组的维数在数组生存期内是不变的,也就是说,可变长数组不是动态的.可以变化的只是数组的大小.可以使用*来定义不确定长的可变长数组. 数组声明中的类型修饰符 在C99中,如果需要使用数组作为函数变元,可以在数组声明的方括号内使用static关键字,这相当于告诉编译程序,变元所指向的数组将至少包含指定

&lt;C语言&gt; C99 Restrict memcpy 内存重叠

https://my.oschina.net/zidanzzg/blog/812887 https://www.cnblogs.com/dylancao/p/9951838.html C语言关键字,编译器优化时使用,不要对编译器撒谎,如果把一个指针定义成Restrict , 编译器会相信你,并对程序进行优化,如果出现内存重叠的问题, 编译器不会替你排查.memcpy()会有内存重叠的问题,memove()会提前帮你检查是否有内存重叠的问题. visual studio 把函数和变量定义成 Res

GCC和C99标准中inline使用上的不同之处。inline属性在使用的时候,要注意以下两点:inline关键字在G

本文介绍了GCC和C99标准中inline使用上的不同之处.inline属性在使用的时候,要注意以下两点:inline关键字在GCC参考文档中仅有对其使用在函数定义(Definition)上的描述,而没有提到其是否能用于函数声明(Declare). 从 inline的作用来看,其放置于函数声明中应当也是毫无作用的:inline只会影响函数在translation unit(可以简单理解为C源码文件)内的编译行为,只要超出了这个范围inline属性就没有任何作用了.所以inline关键字不应该出现