c语言易变的
⑴ c语言中volatile在什么情况下使用
简单的说,你所定义的这个变量,在你程序运行过程中一直会变,你希望这个值被正确的处理,你就得每次从内存中去读这个值,这样就不会有错误了,这个volatile就是这个作用了。
⑵ C语言volatile的问题;回答得好加100分
volatile修饰的变量是可变的,易变的,主要是防止编译器对volatile修饰的变量进行优化,让程序每一次取到变量的实际值,而不是缓存里值。
中断服务子程序中volatile修饰的变量,如果不加volatile修饰,其他地方修改后,中断服务程序中没有修改, 这样是不会获取到改变的值的,volatile就是让中断服务程序每一次都取到指针指向的实际的值。
⑶ c语言volatile是什么意思
“一个定义为volatile的变量是说这变量可能会被意想不到地改变,”
举一个嵌入式开发的例子。在STM32F10x_FWLib 标准函数库对volatile的使用。
这段代码在stm32f10x.h中
当变量为输入输出之时,程序中不一定出现赋值,但变量会根据硬件状态改变。
所以每次要重新从外设寄存器中读取。const的使用可以对比着看
#define __I volatile const /*!< defines 'read only' permissions */
#define __O volatile /*!< defines 'write only' permissions */
#define __IO volatile /*!< defines 'read / write' permissions */
typedef __IO int8_t vs8;
typedef __I int32_t vsc32; /*!< Read Only */
⑷ C语言中Valatile关键字有什么用
volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。下面举例说明。在DSP开发中,经常需要等待某个事件的触发,所以经常会写出这样的程序:
short flag;
void test()
{
do1();
while(flag==0);
do2();
}
这段程序等待内存变量flag的值变为1(怀疑此处是0,有点疑问,)之后才运行do2()。变量flag的值由别的程序更改,这个程序可能是某个硬件中断服务程序。例如:如果某个按钮按下的话,就会对DSP产生中断,在按键中断程序中修改flag为1,这样上面的程序就能够得以继续运行。但是,编译器并不知道flag的值会被别的程序修改,因此在它进行优化的时候,可能会把flag的值先读入某个寄存器,然后等待那个寄存器变为1。如果不幸进行了这样的优化,那么while循环就变成了死循环,因为寄存器的内容不可能被中断服务程序修改。为了让程序每次都读取真正flag变量的值,就需要定义为如下形式:
volatile short flag;
需要注意的是,没有volatile也可能能正常运行,但是可能修改了编译器的优化级别之后就又不能正常运行了。因此经常会出现debug版本正常,但是release版本却不能正常的问题。所以为了安全起见,只要是等待别的程序修改某个变量的话,就加上volatile关键字。
volatile的本意是“易变的”
由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:
static int i=0;
int main(void)
{
...
while (1)
{
if (i) do_something();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
程序的本意是希望ISR_2中断产生时,在main当中调用do_something函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致do_something永远也不会被调用。如果变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。
二、volatile 的含义
volatile总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以死代码消除。但有时这些优化不是程序所需要的,这时可以用volatile关键字禁止做这些优化,volatile的字面含义是易变的,它有下面的作用:
1 不会在两个操作之间把volatile变量缓存在寄存器中。在多任务、中断、甚至setjmp环境下,变量可能被其他的程序改变,编译器自己无法知道,volatile就是告诉编译器这种情况。
2 不做常量合并、常量传播等优化,所以像下面的代码:
volatile int i = 1;
if (i > 0) ...
if的条件不会当作无条件真。
3 对volatile变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到,编译器常常可以省略那个赋值操作,然而对Memory Mapped IO的处理是不能这样优化的。
前面有人说volatile可以保证对内存操作的原子性,这种说法不大准确,其一,x86需要LOCK前缀才能在SMP下保证原子性,其二,RISC根本不能对内存直接运算,要保证原子性得用别的方法,如atomic_inc。
对于jiffies,它已经声明为volatile变量,我认为直接用jiffies++就可以了,没必要用那种复杂的形式,因为那样也不能保证原子性。
你可能不知道在Pentium及后续CPU中,下面两组指令
inc jiffies
;;
mov jiffies, %eax
inc %eax
mov %eax, jiffies
作用相同,但一条指令反而不如三条指令快。
三、编译器优化 → C关键字volatile → memory破坏描述符zz
“memory”比较特殊,可能是内嵌汇编中最难懂部分。为解释清楚它,先介绍一下编译器的优化知识,再看C关键字volatile。最后去看该描述符。
1、编译器优化介绍
内存访问速度远不及CPU处理速度,为提高机器整体性能,在硬件上引入硬件高速缓存Cache,加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺序执行,没有相关性的指令可以乱序执行,以充分利用CPU的指令流水线,提高执行速度。以上是硬件级别的优化。再看软件一级的优化:一种是在编写代码时由程序员优化,另一种是由编译器进行优化。编译器优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令。对常规内存进行优化的时候,这些优化是透明的,而且效率很好。由编译器优化或者硬件重新排序引起的问题的解决办法是在从硬件(或者其他处理器)的角度看必须以特定顺序执行的操作之间设置内存屏障(memory barrier),linux 提供了一个宏解决编译器的执行顺序问题。
void Barrier(void)
这个函数通知编译器插入一个内存屏障,但对硬件无效,编译后的代码会把当前CPU寄存器中的所有修改过的数值存入内存,需要这些数据的时候再重新从内存中读出。
2、C语言关键字volatile
C语言关键字volatile(注意它是用来修饰变量而不是上面介绍的__volatile__)表明某个变量的值可能在外部被改变,因此对这些变量的存取不能缓存到寄存器,每次使用时需要重新存取。该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,而程序通过该变量同步各个线程,例如:
DWORD __stdcall threadFunc(LPVOID signal)
{
int* intSignal=reinterpret_cast<int*>(signal);
*intSignal=2;
while(*intSignal!=1)
sleep(1000);
return 0;
}
该线程启动时将intSignal 置为2,然后循环等待直到intSignal 为1 时退出。显然intSignal的值必须在外部被改变,否则该线程不会退出。但是实际运行的时候该线程却不会退出,即使在外部将它的值改为1,看一下对应的伪汇编代码就明白了:
mov ax,signal
label:
if(ax!=1)
goto label
对于C编译器来说,它并不知道这个值会被其他线程修改。自然就把它cache在寄存器里面。记住,C 编译器是没有线程概念的!这时候就需要用到volatile。volatile 的本意是指:这个值可能会在当前线程外部被改变。也就是说,我们要在threadFunc中的intSignal前面加上volatile关键字,这时候,编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作的循环变为如下面伪码所示:
label:
mov ax,signal
if(ax!=1)
goto label
3、Memory
有了上面的知识就不难理解Memory修改描述符了,Memory描述符告知GCC:
1)不要将该段内嵌汇编指令与前面的指令重新排序;也就是在执行内嵌汇编代码之前,它前面的指令都执行完毕
2)不要将变量缓存到寄存器,因为这段代码可能会用到内存变量,而这些内存变量会以不可预知的方式发生改变,因此GCC插入必要的代码先将缓存到寄存器的变量值写回内存,如果后面又访问这些变量,需要重新访问内存。
如果汇编指令修改了内存,但是GCC 本身却察觉不到,因为在输出部分没有描述,此时就需要在修改描述部分增加“memory”,告诉GCC 内存已经被修改,GCC 得知这个信息后,就会在这段指令之前,插入必要的指令将前面因为优化Cache 到寄存器中的变量值先写回内存,如果以后又要使用这些变量再重新读取。
使用“volatile”也可以达到这个目的,但是我们在每个变量前增加该关键字,不如使用“memory”方便。
⑸ 计算机c语言关键字是什么
1. 数据类型关键字(12个):
(1). char :声明字符型变量或函数
(2). double :声明双精度变量或函数
(3). enum :声明枚举类型
(4). float:声明浮点型变量或函数
(5). int: 声明整型变量或函数
(6). long :声明长整型变量或函数
(7). short :声明短整型变量或函数
(8). signed:声明有符号类型变量或函数
(9). struct:声明结构体变量或函数
(10). union:声明联合数据类型
(11). unsigned:声明无符号类型变量或函数
(12). void :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)
(2)控制语句关键字(12个):
A.循环语句
(1). for: 一种循环语句(可意会不可言传)
(2). do : 循环语句的循环体
(3). while :循环语句的循环条件
(4). break:跳出当前循环
(5). continue:结束当前循环,开始下一轮循环
B.条件语句
(1).if: 条件语句
(2).else :条件语句否定分支(与 if 连用)
(3).goto:无条件跳转语句
C.开关语句
(1).switch :用于开关语句
(2).case:开关语句分支
(3).default:开关语句中的“其他”分支
D.
return :子程序返回语句(可以带参数,也看不带参数)
3. 存储类型关键字(4个):
(1).auto :声明自动变量 一般不使用
(2).extern:声明变量是在其他文件正声明(也可以看做是引用变量)
(3).register:声明积存器变量
(4). static :声明静态变量
4. 其它关键字(4个):
(1).const :声明只读变量
(2).sizeof:计算数据类型长度
(3).typedef:用以给数据类型取别名(当然还有其他作用)
(4).volatile:说明变量在程序执行中可被隐含地改变 1、算法
程序处理数据的流程被称为算法,算法可以用言语描述,也可以用流程图描述.
2、程序
不管用什么语言来表达的对问题的描述,通常都称为程序。
3、数据结构
数据结构是计算机存储、组织数据的方式。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率的算法。数据结构往往同高效的检索算法和索引技术有关。
4、数据类型
数据类型是指数据的内在表现形式。通俗地说,我们把数据加工计算中的特征称为数据类型。
5、字符串
字符串或串(String)是由零个或多个字符组成的有限序列。一般记为 s='a1a266;66;66;an'(n>=0)。它是编程语言中表示文本的数据类型。
通常以串的整体作为操作对象,如:在串中查找某个子串、求取一个子串、在串的某个位置上插入一个子串以及删除一个子串等。两个字符串相等的充要条件是:长度相等,并且各个对应位置上的字符都相等。设p、q是两个串,求q在p中首次出现的位置的运算叫做模式匹配。串的两种最基本的存储方式是顺序存储方式和链接存储方式。
6、表达式
用运算符和括号将运算对象(常量、变量和函数等)连接起来的、符合C语言语法规则的式子,称为表达式。
7、主调函数
在函数中,可以使用函数调用语句,来调用其他函数(称为被调用函数),这时,就把控制转移到被调用函数,而在被调用函数执行完毕或执行到语句return时,又把控制转向原来的函数(称为主调函数).
8、被调函数
9、变量
在程序执行过程中,其值不发生改变的量称为常量,取值可变的量称为变量。
⑹ C语言中的volatile是什么意思
volatile 实例讲解
volatile的本意是一般有两种说法--1.“暂态的”;2.“易变的”。
这两种说法都有可行。但是究竟volatile是什么意思,现举例说明(以Keil-c与a51为例
例子来自Keil FQA),看完例子后你应该明白volatile的意思了,如果还不明白,那只好
再看一遍了。
例1.
void main (void)
{
volatile int i;
int j;
i = 1; //1 不被优化 i=1
i = 2; //2 不被优化 i=1
i = 3; //3 不被优化 i=1
j = 1; //4 被优化
j = 2; //5 被优化
j = 3; //6 j = 3
}
---------------------------------------------------------------------
例2.
函数:
void func (void)
{
unsigned char xdata xdata_junk;
unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;
t1 = *p;
t2 = *p;
}
编译的汇编为:
0000 7E00 R MOV R6,#HIGH xdata_junk
0002 7F00 R MOV R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----
0004 8F82 MOV DPL,R7
0006 8E83 MOV DPH,R6
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 注意
0008 E0 MOVX A,@DPTR
0009 F500 R MOV t1,A
000B F500 R MOV t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
000D 22 RET
将函数变为:
void func (void)
{
volatile unsigned char xdata xdata_junk;
volatile unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;
t1 = *p;
t2 = *p;
}
编译的汇编为:
0000 7E00 R MOV R6,#HIGH xdata_junk
0002 7F00 R MOV R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----
0004 8F82 MOV DPL,R7
0006 8E83 MOV DPH,R6
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
0008 E0 MOVX A,@DPTR
0009 F500 R MOV t1,A a处
000B E0 MOVX A,@DPTR
000C F500 R MOV t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
000E 22 RET
比较结果可以看出来,未用volatile关键字时,只从*p所指的地址读一次
如在a处*p的内容有变化,则t2得到的则不是真正*p的内容。
---------------------------------------------------------------------
例3
volatile unsigned char bdata var; // use volatile keyword here
sbit var_0 = var^0;
sbit var_1 = var^1;
unsigned char xdata values[10];
void main (void) {
unsigned char i;
for (i = 0; i < sizeof (values); i++) {
var = values[i];
if (var_0) {
var_1 = 1; //a处
values[i] = var; // without the volatile keyword, the compiler
// assumes that 'var' is unmodified and does not
// reload the variable content.
}
}
}
在此例中,如在a处到下一句运行前,var如有变化则不会,如var=0xff; 则在
values[i] = var;得到的还是values[i] = 1;
---------------------------------------------------------------------
应用举例:
例1.
#define DBYTE ((unsigned char volatile data *) 0)
说明:此处不用volatile关键字,可能得不到真正的内容。
---------------------------------------------------------------------
例2.
#define TEST_VOLATILE_C
//***************************************************************
// verwendete Include Dateien
//***************************************************************
#if __C51__ < 600
#error: !! Keil 版本不正确
#endif
//***************************************************************
// 函数 void v_IntOccured(void)
//***************************************************************
extern void v_IntOccured(void);
//***************************************************************
// 变量定义
//***************************************************************
char xdata cvalue1; //全局xdata
char volatile xdata cvalue2; //全局xdata
//***************************************************************
// 函数: v_ExtInt0()
// 版本:
// 参数:
// 用途:cvalue1++,cvalue2++
//***************************************************************
void v_ExtInt0(void) interrupt 0 {
cvalue1++;
cvalue2++;
}
//***************************************************************
// 函数: main()
// 版本:
// 参数:
// 用途:测试volatile
//***************************************************************
void main() {
char cErg;
//1. 使cErg=cvalue1;
cErg = cvalue1;
//2. 在此处仿真时手动产生中断INT0,使cvalue1++; cvalue2++
if (cvalue1 != cErg)
v_IntOccured();
//3. 使cErg=cvalue2;
cErg = cvalue2;
//4. 在此处仿真时手动产生中断INT0,使cvalue1++; cvalue2++
if (cvalue2 != cErg)
v_IntOccured();
//5. 完成
while (1);
}
//***************************************************************
// 函数: v_IntOccured()
// 版本:
// 参数:
// 用途: 死循环
//***************************************************************
void v_IntOccured() {
while(1);
}
仿真可以看出,在没有用volatile时,即2处,程序不能进入v_IntOccured();
但在4处可以进入v_IntOccured();
⑺ C语言中关键字volatile是什么意思
volatile的本意是一般有两种说法。1.“暂态的 2.“易变的。这两种说法都有可行。一个定义为volatile的变量是说这变量可能会被意想不到的改变,这样,编译器就不会去假设这个变量的值了。优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。 www.okeycar.com
⑻ c语言中各种语句的用途用法
auto用于说明自动变量,通常不用;volatile(易变的)表示该变量不经过赋值,其值也可能被改变(例如表示时钟的变量、表示通信端口的变量等)。
C语言32个关键字
[code]auto :声明自动变量 一般不使用
double :声明双精度变量或函数
int: 声明整型变量或函数
struct:声明结构体变量或函数
break:跳出当前循环
else :条件语句否定分支(与 if 连用)
long :声明长整型变量或函数
switch :用于开关语句
case:开关语句分支
enum :声明枚举类型
register:声明积存器变量
typedef:用以给数据类型取别名(当然还有其他作用)
char :声明字符型变量或函数
extern:声明变量是在其他文件正声明(也可以看做是引用变量)
return :子程序返回语句(可以带参数,也看不带参数)
union:声明联合数据类型
const :声明只读变量
float:声明浮点型变量或函数
short :声明短整型变量或函数
unsigned:声明无符号类型变量或函数
continue:结束当前循环,开始下一轮循环
for:一种循环语句(可意会不可言传)
signed:生命有符号类型变量或函数
void :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)
default:开关语句中的“其他”分支
goto:无条件跳转语句
sizeof:计算数据类型长度
volatile:说明变量在程序执行中可被隐含地改变
do :循环语句的循环体
while :循环语句的循环条件
static :声明静态变量
if:条件语句 [/code]
⑼ c语言 const volatile int i 这代码有问题没,如果没有,i 是什么属性
没问题,const和volatile这两个类型限定符不矛盾。const表示(运行时)常量语义:被const修饰的对象在所在的作用域无法进行修改操作,编译器对于试图直接修改const对象的表达式会产生编译错误。volatile表示“易变的”,即在运行期对象可能在当前程序上下文的控制流以外被修改(例如多线程中被其它线程修改;对象所在的存储器可能被多个硬件设备随机修改等情况):被volatile修饰的对象,编译器不会对这个对象的操作进行优化。一个对象可以同时被const和volatile修饰,表明这个对象体现常量语义,但同时可能被当前对象所在程序上下文意外的情况修改。
另外,LS错误,const可以修饰左值,修饰的对象本身也可以作为左值(例如数组)。
====
[原创回答团]