00. 目录

文章目录

  • 00. 目录
  • 01. volatile概述
  • 02. volatile应用场景
  • 03. volatile应用示例
  • 04. 嵌入式系统中应用
  • 05. volatile官方说明
  • volatile
  • 06. 附录

01. volatile概述

volatile是C语言中的一个关键字。将变量定义为volatile就表示告诉编译器这个变量可能会被竟想不到地改变,在这种情况下,编译器就不会去假设这个变量的值了,及优化器在用到这个变量是必须每次重新读取他的值。

02. volatile应用场景

在程序中,volatile变量常用于以下几种情况:

  • 并行设备的硬件寄存器(例如:状态寄存器)
  • 在中断服务子程序中会访问到的非自动变量(即全局变量)
  • 多线程应用中被几个任务共享的变量

对于一般变量,其存储的位置是在内存中,但也有可能存储在处理器的寄存器中。在程序中,只要寄存器的内容没有被更改,对变量访问就不需要访问内存,只需要直接使用寄存器的变量。

例如:在程序中可以按照以下的形式定义volatile变量:

void test()
{volatile char temp;
}

当变量temp被定义成volatile类型的时候,它就不会被编译器优化,在每次访问temp变量的时候都将重新在内存中读取它的值。

事实上,在编译器的优化中,类似temp这种建立在函数栈上的变量是不太可能被外部更改的。在程序中,一般容易被更改的变量是指针指向的内容。

03. volatile应用示例

C语言编译器一般都有优化的功能,对代码进行优化。例如:

int tmp, a1, a2;
tmp = (unsigned int *)0x4004;
a1 = *tmp;
a2 = *tmp;

在某些编译器中,这段代码很可能被编译器优化,优化的结果等同如下代码。

int tmp, a1, a2;
tmp = (unsigned int *)0x4004;
a1 = *tmp;
a2 = a1;

这种优化在一般的情况下没有什么错误,但是在特殊的情况下却可能引发错误。例如:第一次读操作(a1 = *tmp)后,*tmp的内容有可能已经被更新,在这种情况下,第2次读操作读出的内容与第一次不一样。原本程序的含义也是在两个不同的时刻读出两个不同的值,但是经过优化后的程序只能读出相同的值。这就需要使用volatile关键字。上述的程序英嘎携程如下形式:

volatile unsigned int *tmp;
int a1, a2;
tmp = (volatile unsigned int *)0x4004;
a1 = *tmp;
a2 = *tmp;

总结

volatile在嵌入式系统中普通用于可能具有并行操作性质的数据,这些变量可能是被外部改变或者内部并行的程序改变。

04. 嵌入式系统中应用

在程序中对GPIO相关寄存器的定义

#define PINSEL0 (*((volatile unsigned long *) 0xE002C000))
#define PINSEL1 (*((volatile unsigned long *) 0xE002C004))
#define PINSEL2 (*((volatile unsigned long *) 0xE002C008))
#define PINSEL3 (*((volatile unsigned long *) 0xE002C00C))

寄存器的定义应该用volatile修饰,避免其在编译过程中被编译器优化,产生意想不到的后果。

05. volatile官方说明

原文如下

volatile

Indicates that a variable can be changed by a background routine.

Keyword volatile is an extreme opposite of const. It indicates that a variable may be changed in a way which is absolutely unpredictable by analysing the normal program flow (for example, a variable which may be changed by an interrupt handler). This keyword uses the following syntax:

volatile data-definition;

Every reference to the variable will reload the contents from memory rather than take advantage of situations where a copy can be in a register.

表明变量能被后台程序修改

关键字volatile和const是完全相反的。它表明变量可能会通过某种方式发生改变,而这种方式是你通过分析正常的程序流程完全预测不出来的。(例如,一个变量可能被中断处理程序修改)。关键字使用语法如下:

volatile data-definition;

每次对变量内容的引用会重新从内存中加载而不是从变量在寄存器里面的拷贝加载。

我的理解:以中断处理程序修改变量解释可能不太合适,以GPIO为例最合适。首先什么是变量?变量就是一块编了地址的内存区域。GPIO的数据寄存器有一个地址,大小一般为32bit,所以这个数据寄存器可以认为就是一个变量,我们可以读写它。如果GPIO设置为输入,修改GPIO数据寄存器这个变量的就是这个GPIO的引脚,不管你如何分析你的程序,你不可能知道这个GPIO数据寄存器里面值是多少,你得读它。你此刻读到数据和下一刻读到的完全可能是不一样的。简单的说就是你要的数据不同步。使用volatile修饰后,会强制你每次引用GPIO寄存器对应的变量时都会去它的寄存器里面读。

防止编译器优化掉操作变量的语句

a.c文件

int main(void)
{volatile char a;a = 5;a = 7;return 0;
}

b.c文件

int main(void)
{char a;a = 5;a = 7;return 0;
}

编译生成汇编文件

deng@itcast:~/tmp$ arm-linux-gcc -S a.c  -o a.s
deng@itcast:~/tmp$ arm-linux-gcc -S b.c -o b.s
deng@itcast:~/tmp$ diff a.s b.s
12c12
<       .file   "a.c"
---
>       .file   "b.c"
deng@itcast:~/tmp$

发现两个汇编文件相差不大,接下来调整优化等级

编译生成汇编文件

deng@itcast:~/tmp$ arm-linux-gcc -O3 -S b.c -o b.s
deng@itcast:~/tmp$ arm-linux-gcc -O3 -S a.c  -o a.s
deng@itcast:~/tmp$ diff a.s b.s
12c12
<       .file   "a.c"
---
>       .file   "b.c"
18c18
<       @ args = 0, pretend = 0, frame = 8
---
>       @ args = 0, pretend = 0, frame = 0
21,24d20
<       sub     sp, sp, #8
<       mov     r3, #5
<       strb    r3, [sp, #4]
<       mov     r3, #7
26,27d21
<       strb    r3, [sp, #4]
<       add     sp, sp, #8
deng@itcast:~/tmp$

可以看到未加volatile修饰的文件b.c,在优化后,汇编对应的a=5;a=7;这两个语句直接优化没了。a=1;a=0;假设a是控制GPIO的语句,原来打算是让GPIO先拉高,再拉低,实现某种时序,结果优化一开,这两句直接废了。这让你在调试硬件的时候会感到莫名其妙。所以这种情况得像a.c那样用volatile来修饰。

防止编译器优化变量的存取对象(memory or register)

a.c文件

int main(void)
{int b;int c;volatile int* a = (int*)0x30000000;b = *a;c = *a;return c + b;
}

b.c文件

int main(void)
{int b;int c;int* a = (int*)0x30000000;b = *a;c = *a;return c + b;
}

生成对应的a.s文件

mov     r3, #805306368
ldr     r2, [r3]        @ b = *a;
ldr     r0, [r3]        @ c = *a;
add     r0, r0, r2      @ b + c;
bx      lr

生成对应的b.s文件

mov     r3, #805306368
ldr     r0, [r3]        @ b = *a;
mov     r0, r0, asl #1      @ b << 2; 也就是 b * 2;也就是 b + b;也就是 add r0, r0, r0(可能这句汇编不合法)
bx      lr

可以看到b.s被优化后,第一次取*a的值时,是从地址0x30000000取出来的(ldr r0, [r3]),第二次就直接没取了,是直接使用了r0的值。这里的r0就是*a的缓存。

访问被volatile修饰的变量时,强制访问内存中的值,而不是缓存中的。

volatile关键词影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错

int main(void)
{int b;volatile int* a = (int*)0x30000000;b = (*a) * (*a);return b;
}

生成对应的汇编

mov     r3, #805306368
ldr     r2, [r3]    ①
ldr     r0, [r3]    ②
mul     r0, r2, r0
bx      lr

程序本意是要计算平方。如果这段代码在运行至①这行汇编时,被调度开了,过了一阵调度回来继续运行②行,此时完全有可能 R2 != R0。那么计算出来的结果R0必然不等于那个平方值。

06. 附录

6.1 volatile官方描述

【嵌入式】C语言中volatile关键字相关推荐

  1. c语言中volatile关键字的作用

    读文章之前 可以先看一下<程序员的自我修养 >第28页 过度优化. volatile 提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直 ...

  2. c语言中volatile关键字

    volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改. 用volatile关键字声明的变量i每一次被访问时,执行部件都会从i相应的内存单元中取出i的值. 没有用 ...

  3. c语言volatile含义,c语言中volatile关键字是什么含义怎么办呢?

    满意答案 yyvalentine 2016.11.26 采纳率:56%    等级:11 已帮助:4891人 C/C++ 中的 volatile 关键字和 const 对应,用来修饰变量,通常用于建立 ...

  4. C语言中volatile关键字与汇编__volatile__

    在gcc中,可以使用 __asm__ 进行汇编语言的内嵌. __volatile__ 表明编译器不要优化代码,后面的指令保持原样. C语言关键字volatile表明某个变量的值在外部可能被改变,因此对 ...

  5. c语言typeof 变量,c语言中typeof关键字

    为什么因为一个关键字而专门写一篇随笔呢?因为这个关键字在Linux里面地位非同一般!这个关键字typeof在linux内核里面使用非常广泛! 下面这两个等效声明,用于声明int类弄的变量a typeo ...

  6. C语言中static关键字详解

    C语言中staic关键字很简单,简单到你的任何一个项目中可以不写一个staic关键字也是没有问题的.写这篇章主要是一下自己的staic的理解和应用,当然在章开头依旧要照本宣科简述一下static关键字 ...

  7. C语言中extern关键字的使用

    C语言中extern关键字的使用,直接上代码. file1.c文件 #include<stdio.h> extern long power(int); int A = 2; int mai ...

  8. 【✊基础不牢,地动山摇のC语言中static关键字✊】

    C语言中static关键字 用static声明限定外部变量与函数,可以将其后声明的对象的作用域限定为被编译源文件的剩余部分.要降对象指定为静态存储,可以在正常的声明之前加上关键字static作为前缀. ...

  9. c语言中extern关键字_了解C语言中的extern关键字

    c语言中extern关键字 In this article, we'll take a look at understanding the extern keyword in C. 在本文中,我们将了 ...

最新文章

  1. linux下卸载自带jdk,重新安装jre运行环境
  2. 数字图像处理:图像就是函数的解读
  3. java实验的技术问题及解决方法,2018-2019-2 20175313 实验一《Java开发环境的熟悉》实验报告...
  4. Visual Studio® 2010 Web Deployment Projects站点编译生成bin同时发表插件
  5. 分贝dB与放大倍数的转换关系
  6. Android WebView对https无响应
  7. Tomcat总体架构
  8. 理解RTMP、HttpFlv和HLS的正确姿势
  9. python集合应用场景_python 集合的应用
  10. 计算理论入门 1.1 命题逻辑
  11. 正则表达式修正符的学习
  12. Gradle下载类库源码
  13. 「 C++ MFC 」“设置线程运行多媒体定时器”教程
  14. Windows电脑上不错的几款图片编辑软件
  15. MATLAB规划和LINGO规划,[数学建模]线性规划与matlab,lingo解法
  16. 计算机打印状态错误,打印机状态错误怎么办 打印机状态错误解决方法【图文】...
  17. 网站关键词选择的四大步骤
  18. 微软Exchange多个高危漏洞通告
  19. vue中input限制只能输入数字
  20. Badboy内置浏览器,提示脚本错误解决方法

热门文章

  1. 【★★★★★模板专区★★★★★】
  2. jquery.easing.js(转)
  3. java 中的访问修饰符
  4. shell 学习笔记(四)
  5. js时间搓化为今天明天_护肤品搓泥怎么办啊啊啊啊啊......
  6. python3.7.2版本怎么安装ipython_Linux升级安装python2.7版本至python3.6版本,系统centos7...
  7. 平安iq测试没通过的话影响入职吗_从外包测试到阿里巴巴,一位三本女生逆袭之路...
  8. 【数据结构总结】第一章:数据结构基本概念
  9. 【数据结构】—顺序表的插入、删除、查找操作
  10. 网络安全等保定级_差异:关键信息基础设施与网络安全等级保护2.0