一、volatile类型限定符

volatile是一个类型修饰符(type specifier),就像我们熟悉的const一样,它是被设计用来修饰被不同线程访问和修改的变量;volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。

volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。简单地说就是防止编译器对代码进行优化。

各种不同的解释:
1、volatile限定符告知计算机,代理(而不是变量所在的程序)可以改变该变量的值。通常,它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。例如:一个地址上可能存储着当前的时钟时间,无论程序做什么,地址上的值都随时间的变化而改变。或者一个地址用于接收另一台计算机传入的信息。

2、volatile属于C语言的关键字,《C Primer Puls》 是这样解释关键字的:关键字是C语言的词汇,由于编译器不具备真正的智能,所以你必须用编译器能理解的术语表示你的意图。开发者告诉编译器该变量是易变的,无非就是希望编译器去注意该变量的状态,时刻注意该变量是易变的,每次读取该变量的值都重新从内存中读取。

3、volatile 影响编译器编译的结果,volatile指出 变量是随时可能发生变化的与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++ 在产生release版可执行码时会 进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化。)

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

5、volatile的本意是**“易变的”** 因为访问寄存器要比访问内存单元快的多,所以编译器一般都会作减少存取内存的优化,但有可能会读错数据。当要求使用volatile声明变量值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。精确地说就是,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问;如果不使用volatile,则编译器将对所声明的语句进行优化。(简洁的说就是:volatile关键词影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错)。

二、编译器优化

1.编译器优化介绍:
由于内存访问速度远不及CPU处理速度,为提高机器整体性能,在硬件上引入硬件高速缓存Cache,加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺序执行,没有相关性的指令可以乱序执行,以充分利用CPU的指令流水线,提高执行速度。以上是硬件级别的优化。
再看软件一级的优化:一种是在编写代码时由程序员优化,另一种是由编译器进行优化。编译器优化常用的方法有:**将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令。**对常规内存进行优化的时候,这些优化是透明的,而且效率很好。由编译器优化或者硬件重新排序引起的问题的解决办法是在从硬件(或者其他处理器)的角度看必须以特定顺序执行的操作之间设置内存屏障(memory barrier),linux 提供了一个宏解决编译器的执行顺序问题。

void Barrier(void)

这个函数通知编译器插入一个内存屏障,但对硬件无效,编译后的代码会把当前CPU寄存器中的所有修改过的数值存入内存,需要这些数据的时候再重新从内存中读出。

2.volatile总是与优化有关,编译器有一种技术叫做数据流分析分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以消除一些代码但有时这些优化不是程序所需要的,这时可以用volatile关键字禁止做这些优化

三、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在嵌入式系统中普通用于可能具有并行操作性质的数据,这些变量可能是被外部改变或者内部并行的程序改变

四、volatile应用场景

1、中断服务程序中修改的供其它程序检测的变量需要加volatile

volatile static int i=0;
int main(void)
{...while (1){if (i) dosomething();}
}
/* Interrupt service routine. */
void ISR_2(void)
{i=1;
}

程序的本意是希望ISR_2中断产生时,在main函数中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。

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

3、存储器映射的硬件寄存器通常也要加voliate,因为每次对它的读写都可能有不同意义。

#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官方说明

volatile官方描述

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

volatile data-definition;

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

个人理解:变量就是一块编了地址的内存区域,GPIO的数据寄存器有一个地址,大小一般为32bit,所以这个数据寄存器可以认为就是一个变量,我们可以读写它。如果GPIO设置为输入,修改GPIO数据寄存器这个变量的就是这个GPIO的引脚,此刻读到数据和下一刻读到的完全可能是不一样的。简单的说就是前后数据不同步。使用volatile修饰后,会强制你每次引用GPIO寄存器对应的变量时都会去它的寄存器里面读。

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

六、面试问题

1)一个参数既可以是const还可以是volatile吗?解释为什么

答:是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2)一个指针可以是volatile 吗?解释为什么。

答:是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。

3)下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?如果不能,试回答存在什么问题:

int square(volatile int *ptr)
{return ((*ptr) * (*ptr));
}

这段代码是个恶作剧。这段代码的目的是用来返指针ptr指向值的平方,但是,由于ptr指向一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int* &ptr)//这里参数应该申明为引用,不然函数体里只会使用副本,外部没法更改
{int a,b;a = *ptr;b = *ptr;return a*b;
}

由于*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:

long square(volatile int*ptr)
{int a;a = *ptr;return a*a;
}

参考资料:
http://tigcc.ticalc.org/doc/keywords.html#volatile
https://blog.csdn.net/tigerjibo/article/details/7427366

C语言volatile类型限定符详解相关推荐

  1. C语言各种类型之间转换详解

    目录 1. C基本类型变量 2. 符号扩展 2.1 规则一: 2.2 规则二: 2.3 规则三: 3. 零扩展 4. 长类型转换为短类型 5. 自动类型转换 5.1 赋值时的类型转换 5.2 运算时的 ...

  2. C语言类型限定符(type specifier)(一)——volatile详细教程

    前言:C语言有几个类型限定符(type specifier),如C90中的const和volatile,C99中的restrict,C11中的_Atomic,除了const以外,很多书籍对于其他几个修 ...

  3. C语言中的类型限定符有哪些?

    C语言中有一些关键字起到了限定的作用,他们被称为限定符,那么在C语言中共有几个限定符呢?他们的作用分别是什么呢?本文主要介绍C中4个限定符 1.const2.volatile3.restrict4._ ...

  4. c语言restrict和const,C语言中const、volatile、restrict等类型限定符的区别

    auto.register.static.extern是属于存储类修饰符.在声明时,存储类修饰符最多只能使用一个,而且无法用在typeof声明中. 而类型限定符是指const.volatile.res ...

  5. C语言中的类型限定符.const限定符

    目录 1.1const限定符 1.1.1const限定符修饰普通对象 1.1.2const限定符修饰数组元素 1.1.3const限定符修饰指针类型对象 1.1.4const限定符修饰函数形参类型为数 ...

  6. 学习笔记9-C语言-传参、类型限定符、递归

    函数传参: 1.形参变量属于它所在的函数,出了该函数就不能使用 2.实参与形参之间都是以赋值的形式进行数据传递(值传递) 3.return 其实是把返回值数据放置到一个公共的区域(函数和函数调用者), ...

  7. (*(volatile unsigned int *))详解

    (*(volatile un 从日剧看日本单身狗现状 登录注册 阿拉丁神丢的博客 http://blog.sina.com.cn/ddlovetechnology [订阅][手机订阅] 首页博文目录图 ...

  8. 大二c语言期末考试题库及详解答案,大学C语言期末考试练习题(带详解答案)...

    <大学C语言期末考试练习题(带详解答案)>由会员分享,可在线阅读,更多相关<大学C语言期末考试练习题(带详解答案)(55页珍藏版)>请在金锄头文库上搜索. 1.一. 单项选择题 ...

  9. C语言 中的 数据类型 超详解

    C语言 中的 数据类型 超详解 一.整型(int.short.long.long long) 1.有符号整型 有符号整型的数据类型通常包括 int.short.long.long long 四种,因为 ...

最新文章

  1. RectF Rect用法
  2. 机器大神 Michael Jordan 教授主题演讲:机器学习——创新视角,直面挑战》
  3. jQuery/javascript实现简单网页计算器
  4. Android 引用模块中的类,解决Android项目中找不到Module中的封装类或引用的第三方类库...
  5. 教你如何做出自己想要的PHP Docker镜像
  6. 分组(recyclerview嵌套)
  7. docker內安装TOPT
  8. kill -9 和 kill -15 的区别
  9. 【原】expdp参数CONTENT
  10. Android CTS测试
  11. 网络工程师项目管理关键路径和松弛时间计算
  12. 聚美上市后将往何方:服装特卖和100%透明的化妆品渠道
  13. 图像颜色空间转换--RGB to Lαβ
  14. laravel 请求出现 post The page has expired due to inactivity.
  15. 51单片机三线串行驱动12864液晶
  16. 浅谈综述论文:文献综述
  17. 2020全国大学生数学建模竞赛穿越沙漠第二问求解方法
  18. FZU 2231 平行四边形数
  19. C# 编程入门第五课,VS2019程序调试,for循环,水仙花数,Console.Write,又一种类型变换,三元表达式,产生随机数
  20. 读《致在大学里感到迷茫的你》有感

热门文章

  1. STM32F40x 红外遥控器
  2. 基于Linux的视频监控系统构建方法
  3. 第二章、质量管理体系综述
  4. 3月21日互联互通社区科技沙龙
  5. 【SQL Server】数据库开发指南(三)面向数据分析的 T-SQL 编程技巧与实践
  6. [转]深度分析中国移动、中国联通、中国电信的3G胜算
  7. html5 围住神经猫,围住神经猫4步攻略 玩法技巧详解
  8. 史上最全的10类常用软件测试工具全都在这(共60+款工具)
  9. 2019HDU多校第七场 HDU6646 A + B = C 【模拟】
  10. 渐变清新年终总结PPT模板