c语言中volatile的作用分析(一)

1. 背景
在嵌入式领域,我们往往有的时候需要根据一个标记 flag或者变量(全局的)来进行不同策略的程序执行。
在非编译器优化的形式下debug模式下,不会出任何问题。
但是我们对外发布的时候,往往都是 release版本,也就是进行了编译器优化,这个时候程序可能就会不同。

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

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. 一般用处
1.并行设备的硬件寄存器
比如对一个地址寄存器重复写多次值,编译器可能只用你最后一次的值;
2.变量在中断被修改
主程序就会判断变量没有被修改,就直接寄存器读取值,但是实际上这个值可能已经在外界被修改;
3.多线程共享标志
和中断类似,变量被改变,但是当前线程没有改变,编译器就会把该变量放到寄存器;
4.那些硬件时序相关
嵌入式设备对硬件进行启动、停止都是有其严格时序的,某一个寄存器先赋值为1再赋值为2最后赋值为1;
所以volatile就显得很重要

5. const 和 volatile冲突吗

  1. const表明修饰的变量不会出现在赋值符号的左边;
  2. volatile表示使用变量的时候直接从内存拿;
  3. 二者是不影响的,可能当前文件的确不会修改const变量,但是其他文件可能会,为了防止编译器优化,又加了volatile;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来为您详细介绍一下如何设计一个C语言词法分析器。 1. 任务 C语言词法分析器的任务是将输入的C代码转化为词法单元序列,识别其的标识符、关键字、运算符、常量等,并将其分类输出。 2. 正则式表示 C语言的词法单元可以使用正则表达式进行描述。以下是C语言常见词法单元的正则表达式: - 标识符:[a-zA-Z_][a-zA-Z0-9_]* - 关键字:auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while - 运算符:+|-|*|/|%|++|--|==|!=|<|>|<=|>=|&&|\|\||!|&|\||^|~|<<|>> - 常量:整数常量|浮点数常量|字符常量|字符串常量 3. 架构设计 C语言词法分析器的架构设计包括以下几个部分: (1)输入接口:负责读取源代码文件,将代码转化为字符流输入给词法分析器。 (2)词法分析器:根据C语言词法单元的正则表达式和自动机算法,将字符流转化为词法单元序列,并将其输出。 (3)符号表:用于记录词法单元的属性值,如标识符的名称、类型等。 (4)输出接口:将词法单元序列输出给语法分析器进行后续处理。 4. 实现步骤 (1)读取源代码文件,将代码转化为字符流输入给词法分析器。 (2)使用正则表达式描述C语言的各种词法单元,将其转化为NFA或DFA自动机。 (3)根据自动机算法,对输入的字符流进行扫描,并将其转化为词法单元序列。 (4)对于每个词法单元,根据其类型和属性值,将其记录到符号表。 (5)将词法单元序列传递给语法分析器进行后续处理。 以上是一个基本的C语言词法分析器的实现步骤。实际开发,还需要处理一些特殊情况,如注释、空格、换行符等,以及优化词法分析器的性能和准确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值