c语言中volatile的关键字作用

c语言中volatile的关键字作用

目录

狄泰学院,唐老师的课学习而来

1. 背景

​ 在嵌入式领域,我们往往有的时候需要根据一个标记 flag或者变量(全局的)来进行不同策略的程序执行。

​ 在非编译器优化的形式下 debug模式下,不会出任何问题

​ 但是我们对外发布的时候,往往都是 release版本,也就是进行了编译器优化,这个时候程序可能就会不同

2. 编译器优化

  1. c语言中,编译的步骤是 先编译后链接的,那么在编译一个文件的时候,这个文件使用了外部的变量,仅仅需要声明就可以,等到链接的时候回去查找,所以编译器优化的时候,都是针对于当前文件进行优化
  2. 编译器 回去查找 当前文件中那些 “不会改变的变量” (比如const 全局变量)
  3. 为了效率的提高,编译器对这种 “不会改变的变量” 进行变量值的缓存
  4. 缓存的方式为:把变量的值从内存读入寄存器
  5. 之后每次访问变量的时候 都直接那寄存器的那个值,而不会去内存地址取变量值

3. 例子

​ main.c 为主程序,不断判断其他文件中的一个全局变量的值(这就类似我们 主程序判断其他 设备状态一样

​ device.c 为设备程序,开辟一个线程,在5s后对全局变量的值进行修改

​ main.c的代码

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
extern const int g_status;
extern  void launch_device();
int main() {
	launch_device();

	while(g_status == 0 ) {
		sleep(1);
		printf("launch_device g_status is %d\n", g_status);
	}    
	printf("launch_device end  g_status is %d\n", g_status);
    return 0;
}

​ device.c的代码

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int g_status = 0;

void* func1(void* p) {
	sleep(5);
	g_status = 1;
	printf("init_device() - device status : g_status = %d\n", g_status);
}

void launch_device(void) {
	pthread_t id;
	pthread_create(&id,NULL,func1,NULL);
}

看起来是很平常的,主程序不断判断g_status,当为1就执行结束

编译:gcc main.c device.c -lpthread -o test

运行:./test

看输出结果

launch_device g_status is 0
launch_device g_status is 0
launch_device g_status is 0
launch_device g_status is 0
init_device() - device status : g_status = 1
launch_device g_status is 1
launch_device end  g_status is 1

完全符合预期,这也就是 我们所谓的 debug模式

下面让我们加上编译优化 gcc里面最高的是 -O3

编译:gcc -O3 main.c device.c -lpthread -o test

运行:./test

看输出结果

launch_device g_status is 0
launch_device g_status is 0
launch_device g_status is 0
launch_device g_status is 0
init_device() - device status : g_status = 1
launch_device g_status is 0
launch_device g_status is 0
launch_device g_status is 0

程序一直在运行,不停止

造成这样的原因就是 编译器进行了优化,看见了main.c中不会对g_status进行更改,那就把这个值存在寄存器中,以后直接拿值

但是实际上g_status值在其他文件是会更改的!!!

4. 解决办法 :Volatile

​ 作用:修饰可能被意外改变的变量(内存)

1. volatile 修饰的变量是易变的

2. 编译器警告指示字
3. 告诉编译器每次去内存中取变量值

然后对上面的例子中的main.c代码改一下

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
extern const volatile int g_status;
extern  void launch_device();
int main() {
	launch_device();

	while(g_status == 0 ) {
		sleep(1);
		printf("launch_device g_status is %d\n", g_status);
	}    
	printf("launch_device end  g_status is %d\n", g_status);
    return 0;
}

编译:gcc -O3 main.c device.c -lpthread -o test

运行:./test

输出结果

launch_device g_status is 0
launch_device g_status is 0
launch_device g_status is 0
launch_device g_status is 0
init_device() - device status : g_status = 1
launch_device g_status is 1
launch_device end  g_status is 1

符合预期

5. const 和 volatile冲突吗

1. const 表明修饰的变量不会出现在赋值符号的左边

2. volatile表示使用变量的时候直接从内存拿
3. 二者是不影响的,可能当前文件的确不会修改const变量,**但是其他文件可能会,为了防止编译器优化,又加了volatile**
上一篇:VsCode配置C++开发环境


下一篇:关于Mac中vscode无法从控制台输入的问题