volatile关键字
volatile关键字的英文意思是易变的,不稳定的。一般用于修饰变量。
volatile的使用场合:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
这是区分C程序员和嵌入式系统程序员的最基本的问题:嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所有这些都要求使用volatile变量。
假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是真正懂得volatile完全的重要性。
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?如果不能,试回答存在什么问题:
int square(volatileint *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。
3). 这段代码是个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatileint * ptr)
{
inta, b;
a = *ptr;
b = *ptr;
return a*b;
}
由于*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:
long square(volatileint * ptr)
{
int a;
a = *ptr;
return a*a;
上面说了volatile的部分用法,volatile用来修饰变量是经常见到的,比如说需要读取外接的几个输入的值,那么需要去读取对应的输入的IO口的状态寄存器,如果在程序中将外设的这个寄存器映射在某一个地址,那么读取这个地址里面的内容就可以得到寄存器的值了,这个时候就可以使用volatile来解决编译器可能错误优化的问题了。例如:
#define rADCCON (*( volatile unsigned *)0x1d30000)
还有一种volatile的用法就是来修饰函数的返回值,比如:
volatile void func() {
...
}
这样做的目的是:volatile修饰不返回函数,比如函数体里面有exit或者死循环之类的东西。这样该函数被调用的时候不用把返回地址压入堆栈,(当然还可能有其他),代码得到优化。
volatile关键字的英文意思是易变的,不稳定的。一般用于修饰变量。
volatile的使用场合:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
这是区分C程序员和嵌入式系统程序员的最基本的问题:嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所有这些都要求使用volatile变量。
假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是真正懂得volatile完全的重要性。
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?如果不能,试回答存在什么问题:
int square(volatileint *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。
3). 这段代码是个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatileint * ptr)
{
inta, b;
a = *ptr;
b = *ptr;
return a*b;
}
由于*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:
long square(volatileint * ptr)
{
int a;
a = *ptr;
return a*a;
}
注:前半部分内容摘自百度百科:http://baike.baidu.com/view/608706.htm?fr=aladdin
上面说了volatile的部分用法,volatile用来修饰变量是经常见到的,比如说需要读取外接的几个输入的值,那么需要去读取对应的输入的IO口的状态寄存器,如果在程序中将外设的这个寄存器映射在某一个地址,那么读取这个地址里面的内容就可以得到寄存器的值了,这个时候就可以使用volatile来解决编译器可能错误优化的问题了。例如:
#define rADCCON (*( volatile unsigned *)0x1d30000)
还有一种volatile的用法就是来修饰函数的返回值,比如:
volatile void func() {
...
}
这样做的目的是:volatile修饰不返回函数,比如函数体里面有exit或者死循环之类的东西。这样该函数被调用的时候不用把返回地址压入堆栈,(当然还可能有其他),代码得到优化。