static与volatile的用法
static
1、概述
static 声明的变量在C语言中有两方面的特征:
1)、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
2)、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
2、问题:Static的理解
关于static变量,请选择下面所有说法正确的内容:
A、若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
B、若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
C、设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题;
D、静态全局变量过大,可能会导致堆栈溢出。
答案与分析:
对于A,B:根据本篇概述部分的说明b),我们知道,A,B都是正确的。
对于C:根据本篇概述部分的说明a),我们知道,C是正确的(所谓的函数重入问题,下面会详细阐述)。
对于D:静态变量放在程序的全局数据区,而不是在堆栈中分配,所以不可能导致堆栈溢出,D是错误的。
因此,答案是A、B、C。
3、问题:不可重入函数
曾经设计过如下一个函数,在代码检视的时候被提醒有bug,因为这个函数是不可重入的,为什么?
unsigned int sum_int( unsigned int base )
{
unsigned int index;
static unsigned int sum = 0; // 注意,是static类型的。
for (index = 1; index <= base; index++)
{
sum += index;
}
return sum;
}
答案与分析:
所谓的函数是可重入的(也可以说是可预测的),即:只要输入数据相同就应产生相同的输出。
这个函数之所以是不可预测的,就是因为函数中使用了static变量,因为static变量的特征,这样的函数被称为:带“内部存储器”功能的的函数。因此如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量,这种函数中的static变量,使用原则是,能不用尽量不用。
将上面的函数修改为可重入的函数很简单,只要将声明sum变量中的static关键字去掉,变量sum即变为一个auto 类型的变量,函数即变为一个可重入的函数。
当然,有些时候,在函数中是必须要使用static变量的,比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。
volatile
volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
volatile译为:易变的。在前面加上关键字volatile之后告诉编译器,用volatile修饰的变量是易变的,不要对此变量进行优化,每次都要到变量的地址中去读取变量的数据,但正因为这样,才是保持了变量的原样,因为变量已经发生改变了,你却操作的是没有变化时的数据,这样才让变量失去了本应该保持的属性。
eg:
int a=1;
a=2;
a=3;
....
编译器看到这样的代码,会觉得a的值只有a=3才有意义,所以把a存储在一个寄存器中,每次遇到a都在这个寄存器中去读取数据,但是a是可能改变,比如中断或者多线程的时候。这个有可能你测试它又是正确的,因为随着你的优化等级提高,生成的汇编代码会有很大不同,如果基础不够扎实,代码的鲁棒性就会减弱,要想不这样,那么需要程序员有足够扎实的基本功。
1.我们先看volatile第一个应用场景,在中断服务函数中的使用。
/* main.c */
int flag=0;
int main(void)
{
if(flag==1)
{do somethings}
if(flag==2)
{do somethings}
return 0;
}
/* interrupt*/
void NVIC_Handler(void)
{
flag=1;
}
在这种情况下,编译器可能会对其做优化,虽然中断服务函数改变了flag的值,但是编译器并没有在变量内存中去读取,而是在寄存器中读取了flag之前的缓存数据。在中断函数中的交互变量,一定要加上volatile关键字修饰,这样每次读取flag的值都是在其内存地址中读取的,确保是我们想要的数据。
2.多任务环境下各任务间共享的标志应该加volatile。原因其实和上面中断一样,要共享标志,又不想让编译器优化了这一点,需要加上该修饰词。
3.存储器映射的硬件寄存器通常也要加voliate,因为每次对它的读写都可能有不同意义。
以STM32为例,寄存器中的数据也是时刻在变化的,我们也不想编译器优化这一点,所以在库函数中我们可以看到这样的代码。

这是寄存器的结构体,我们查看前缀__I 和__IO到底是什么?

可以看到,在寄存器的映射中,也需要volatile,因为寄存器的值也是可能随时更改的。
通过上面,我们也应该明白那个问题。
一个参数既可以是const还可以是volatile吗?
可以的,例如只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。软件不能改变,并不意味着我硬件不能改变你的值,这就是单片机中的应用。
static与volatile的用法的更多相关文章
- volatile的用法
在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized ...
- 《OOC》笔记(1)——C语言const、static和extern的用法
<OOC>笔记(1)——C语言const.static和extern的用法 C语言中const关键字用法不少,我只喜欢两种用法.一是用于修饰函数形参,二是用于修饰全局变量和局部变量. 用c ...
- C++ 中 static 与 const 的用法及对比
在这个学习过程中我对 static 及 const 的使用时常会混淆,因此整理,加深记忆 一.类的静态成员 如果某个属性为整个类所共有,不属于任何一个具体对象,则采用 static 关键字来声明静态成 ...
- c中常用的关键字static const volatile
在C语言中,关键字static有三个明显的作用:1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变.2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数 ...
- [Android Pro] static 和 Volatile 的区别
reference to : http://blog.sina.com.cn/s/blog_4e1e357d0101i486.html static也是各个业务方可以去全局修改: volatile是处 ...
- static \ const \ volatile 的含义
1.static 在函数体内,一个被声明为静态的变量在这一函数被调用的过程中维持其值不变 在模块内(函数体外),一个被声明为静态的变量可以被模块内的所有函数访问,但不能被模块外的其他函数访问,即它是一 ...
- static和extern的用法小结
以前写程序是,基本不管static和extern,一个工程文件也只有一个c文件.今天尝试用多个文件来写,自然就涉及到这两个关键词的使用,自己查了些资料,并且做了些实验,总结如下. extern的用法 ...
- Java 中 static 和 volatile 关键字的区别?
static指的是类的静态成员,实例间共享 volatile跟Java的内存模型有关,线程执行时会将变量从主内存加载到线程工作内存,建立一个副本,在某个时刻写回.valatile指的每次都读取主内存的 ...
- const,static,volatile关键字的作用
const关键字: 1.欲阻止一个变量被改变,可使用const,在定义该const变量时,需先初始化,以后就没有机会改变他了: 2.对指针而言,可以指定指针本身为const,也可以指定指针所指的数据为 ...
随机推荐
- iOS -- Effective Objective-C 阅读笔记 (5)
1: 理解 '对象等同性' 概念 理解: 根据'等同性' 来比较对象是一个非常有用的功能, 不过按照 == 操作符比较出来的结果未必是我们想要的, 因为该操作比较的是两个指针本身, 而不是其所指的对象 ...
- 【python】笔记
texts=[[word for word in document.lower().split() if word not in stoplist] for document in documents ...
- 使用Pycharm创建一个Django项目
在使用python写脚本一段时间后,想尝试使用Django来编写一个python项目,现做以下记录备忘: 1.创建项目 如果本地没有安装与所选python版本对应Django版本,pycharm会自动 ...
- Mybatis注意点之#与$区别
动态 SQL 是 mybatis 的强大特性之一,也是它优于其他 ORM 框架的一个重要原因.mybatis 在对 sql 语句进行预编译之前,会对 sql 进行动态解析,解析为一个 BoundSql ...
- 解决使用maven的java web项目导入后出现的有关问题 -cannot be read or is not a valid ZIP file
解决使用maven的java web项目导入后出现的有关问题 -cannot be read or is not a valid ZIP file 错误问题:虽然查找repository目录下是有 ...
- C#学习-接口
众所周知,电脑有拍照和播放光碟的功能. 现在有一个TakingPhoto类,它提供了拍照的功能:还有一个PlayVCD类,它提供了播放光碟的功能. 电脑同时具有着两个类提供的功能,因此我们希望定义一个 ...
- drop、truncate和delete的区别
TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行.但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源 ...
- python 通过js控制滚动条拉取全文 通过psutil获取pid窗口句柄,通过win32gui使程序窗口前置 通过autopy实现右键菜单和另存为操作
1.参考 利用 Python + Selenium 自动化快速截图 利用 Python + Selenium 实现对页面的指定元素截图(可截长图元素) 使用python获取系统所有进程PID以及进程名 ...
- Android CameraManager 类
先看代码: private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private CameraManager ...
- 学习django就看这本书了!django book 2.0中文版
所属网站分类: 资源下载 > python电子书 作者:熊猫烧香 链接:http://www.pythonheidong.com/blog/article/29/ 来源:python黑洞网 dj ...