c语言const、volatile问题小结

≡放荡痞女 提交于 2020-03-30 11:45:57

之前百度面试的时候被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

2

3

4

int square(volatile int *ptr)

{

    return ((*ptr) * (*ptr));

}

下面是答案:

1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2). 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。

3). 这段代码是个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:

 

1

2

3

4

5

6

7

int square(volatile int* &ptr)//这里参数应该申明为引用,不然函数体里只会使用副本,外部没法更改

{

    int a,b;

    a = *ptr;

    b = *ptr;

    return a*b;

}

由于*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:

 

1

2

3

4

5

6

long square(volatile int*ptr)

{

    int a;

    a = *ptr;

    return a*a;

}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!