目录
- 一、前言
- 二、最易变的关键字—volatile
- 1、volatile 总体阐述
- 2、CPU运算与内存覆盖
- 3、线程与执行流
- 4、volatile修饰变量
- 三、总结
一、前言
大家好,欢迎来到C语言深度解析专栏—C语言关键字详解第五篇,在本篇中我们将会介绍C语言当中的另外一个重要的关键字 volatile ,相信大家在看完这篇博客后会对 volatile 这个关键字的用法及注意事项有一个系统、全面的认识。
二、最易变的关键字—volatile
volatile 是易变的、不稳定的意思。很多人根本就没见过这个关键字,不知道它的存在。也有很多程序员知道它的存在,但从来没用过它。也正是因为它冷门,所以在面试C语言相关问题的时候,volatile 和 static 、const 这两个关键字一样成为了最经常被问到的问题。
1、volatile 总体阐述
volatile 关键字和 const 一样是一种类型修饰符,用它修饰的变量表示可以被某些编译器,未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。这是C深度剖析中对 volatile 的解释,现在我们可能对这段描述并不感冒,没关系,我们下面对 volatile 进行详细介绍之后再回过头来看它,相信那时,大家对这段话会有不一样的感受。
2、CPU运算与内存覆盖
在介绍 volatile 之前,我们需要先介绍一下CPU运算与内存覆盖相关概念。
>如图:这里我们用 flag 标记了一个循环,编译器在执行这条语句的时候为了对循环进行逻辑判断需要CPU参与,而CPU进行逻辑判断的时候是先将变量 flag 加载到寄存器中,再判断循环条件是否为真,为真再执行循环语句,但是我们这里并没有任何东西能够修改我的循环变量flag的值,也就是是,我们定义了一个死循环,那么,为了将这个循环进行下去,CPU就需要不断地将变量flag从内存加载到寄存器中进行逻辑判断,显然,这样效率很低,所以,为了提高效率,CPU会直接将 flag 放在寄存器中,以后CPU每次检测时直接从寄存器中读取 flag 的值,不再从内存中读取,这种情况也被称为 “内存覆盖”。
3、线程与执行流
在我们当下来看,这种做法显然没什么问题,因为我们现在写的代码都是单线程的,只有一个执行流来执行程序,但是一个程序并不是只能是单线程的,当我们以后在学习多线程、多进程代码的时候就会知道,循环变量 flag 是有可能在 while 外部被其他值修改的,当一个和 while 并行存在的逻辑将 flag 改为0时,问题就来了,因为CPU是直接从寄存器中读取 flag 的值进行 while 循环的逻辑判断的,所以当另一个逻辑将 flag 改为0时,while 循环并不会停止,而是会继续执行其中的代码块,从而造成程序逻辑上的错误(关于多线程、多进程的知识大家现在只需要了解就可以,以后会细学)。
4、volatile修饰变量
代码语言:javascript复制那么,为了在某些特殊的情况下出现上述的问题,我们应该怎么办呢?我们可以直接在 flag 变量前面加上 volatile 关键字,让CPU不要对 flag 进行优化,每次都继续从内存当中读取 flag 的值。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
volatile int flag = 1;
while (flag) {
;
}
return 0;
}
这就是 volatile 关键字修饰变量的作用:让编译器不对被 volatile 修饰的变量进行优化,从而达到稳定访问内存的目的。 注意:虽然 volatile 叫做易变关键字,但这里仅仅是描述它修饰的变量可能会变化,要编译器注意,并不是要求被它修饰的变量必须 变化!这点要特别注意。
三、总结
- 编译器在运行程序时为了提高运行效率,可能会对某些程序进行优化,其中就有可能会将某些需要反复从内存中读取的变量直接放在寄存器当中(这点大家可以和 register 来对比一下),从而造成在多线程情况下程序逻辑错误的问题,为了解决或者避免此类错误,C语言中定义了 volatile 关键字。
- volatile 关键字修饰的变量表示可以被某些编译器,未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
如果你觉得这篇文章对你有帮助的话,还请给个三连支持一下