之前百度面试的时候被volatile虐了,内核中很多地方也会用到,这个面试的时候出现概率太大了,所以搜集了一些结果供大家参考,大部分是百度到的,说得挺明确的,以后读代码的时候遇到了再更新。
百度知道有人提如下问题:
#include "stdio.h" int main(void){ const char i = 1; char * j = (char *)&i; printf("%d,%d,%p,%p\n",i,*j,&i,j);//1,1 *j = 2; printf("%d,%d,%p,%p\n",i,*j,&i,j);//1,2 *j = 3; printf("%d,%d,%p,%p\n",i,*j,&i,j);//1,3 }
为什么i没有改变呢,各位运行也会发现,他们的地址都是一样的。
醉了,我debug的时候,i也是随着j改变的,但是输出时,i就变成1了。希望各位能够从编译的角度上说一下这个问题。
我对答案有稍微修改,对比vc编译结果差不多所以就引用别人的图了
1.这是有const修饰与无const修饰的汇编代码
变量i存储在eax寄存器中,有const修改表达寄存器的值不允许被修改
第22行的时候,对*j=2;赋值时,有const修饰的会对edx进行操作
而没有const进行修饰的就是直接对eax进行操作.
至于编译器调试模式下,看见的i的值变成2,是因为编译器看见的是edx,实际输出的是1,参见第16行加了const之后,优化了printf函数的第一个%d对应的i,直接传的1,而没有const的时候,是传的i地址所取到的值进去,说到底还是编译优化问题,也是看内核看到这里搜到你的问题,加volatile确实可以强制编译器传i的地址去取值,在调试器里面看到的是寄存器的值所以是变化的
2.站在逻辑的角度上,const修饰符就是限定变量的值不能被更改.至于后台怎么实现,你可以看看汇编,一条简单的赋值表达式也是由三条汇编语言实现的.
3.const就是限定不能被更改,限定的是变量,变量不能被修改,限定的是指针,指针值不能被修改
类中限定的是方法,方法不能更改成员变量的值,限定的是返回,返回值不能被修改
volatile被设计用来修饰被不同线程访问和修改的变量。如果不加入volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
这是区分C程序员和嵌入式系统程序员的最基本的问题:嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所有这些都要求使用volatile变量。不懂得volatile内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是真正懂得volatile完全的重要性。
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?如果不能,试回答存在什么问题:
|
下面是答案:
1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。
3). 这段代码是个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
|
由于*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:
|
来源:https://www.cnblogs.com/hmxb/p/4901447.html