C中const的实现机制

571 2023-02-02 11:10

同学去参加笔试,遇到一个关于const的问题,大致如下:

有如下代码,问该段代码是否可以编译通过,是否可以运行,运行结果是什么。

    const int a = 1;
     
    int* b = (int*)&a;
     
    *b = 31;


以上代码是可以编译通过并且能够成功运行的,运行后a b的值均为31。

这道题的关键在于C语言中const的实现机制。在C中,const修饰的变量不能够被修改,在反汇编后,有const修饰的变量和没有const修饰的变量的结果是一样的。但是如果在程序中对const变量进行修改,编译时就会报错,如:

    const int a = 1;
     
    a = 2;


这段代码编译时就会报错,error C2166: l-value specifies const object。

那么,C到底是如何实现const的呢。


在最初的C标准中,并没有const,只是在有了C++后,才将const加入C的标准中。而实际上C对const变量并没有做过多的处理,编译后const和普通变量没有区别,只是在编译的过程中,编译器会检查代码中是否有对const变量进行修改的代码,如果有则向用户报错。在编译过后,const变量就和普通变量相同了。而且,如果使用memset去修改const变量的内容,也完全没有问题,这就可以看出const修饰是属于编译层面的限制,一般不会涉及到运行层面。在C中,const是用于明确的标识出变量或者函数不能被修改,而且这种限制在编译层面进行约束。


因此在最初那道题,a是const变量,整个代码段没有对a进行修改,因此编译可以通过,编译通过后a和普通变量相同,变量b做修改完全可以,因此该段代码编译可以通过,也可以成功运行。
————————————————
版权声明:本文为CSDN博主「RJS_April」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luoyeaijiao/article/details/7982385

 

#include <iostream>
using namespace std;

void main()
{
int c = 2;
const int a = c;
//const int a = 2;
int *b = (int *)&a;
*b = 3;
cout << a <<endl;
cout << (*b) <<endl;
}

这样就输出的是3,3了。。。。求原因

firendlys 2012.09.16

原因在于对const 类型,编译器有3种不同的处理.
1. 对于直接已知值的int,long,short,char 类型以及其unsigned版本,即 const int a=2; 这种,编译器编译程序之后,程序中所有a出现的地方,全部自动替换成2. 所以,就出现了对于 *b=3 ,在 const int a=2 ;中不会修改a,而在 const int a=c; 中则会修改 a的情况.
2.对于字符串. 类似 const char *a="abc"; 这种,同样是不能修改的,不过原因就不再是上面那个,而是因为这个 "abc" 在编译之后是放在程序的"常量段",这部分是执行文件的一部分,运行期间不可修改,如果强制修改,就会出现 内存读写错误:0x000005不可写 这种错误.
3.就是文章提到的这种情况,会 *b=3 会修改const的限制,原因也如文章中所说一致,这个限制只是编译期间限制,运行期间不受影响.对于上面没有提到的类型(包括float,double,以及自定义类型),都会作这种处理.

其实怎么用文字分析,都不如看汇编代码来的清晰和直接...

 

64:       const int a = 2;
00403B3D   mov         dword ptr [ebp-30h],2
65:       int *b = (int *)&a;
00403B44   lea         ecx,[ebp-30h]
00403B47   mov         dword ptr [ebp-34h],ecx
66:       int c=a;
00403B4A   mov         dword ptr [ebp-38h],2 // 这里,c=a,给的不是a的地址,而是直接是a的值.
//因此,即使 *b 的值变了, c 依然都是a最原始的值...
67:       cout << a ;
00403B51   push        2 // 这里也一样,只是a的原始值,而不是*b的值...
00403B53   mov         ecx,offset std::cout (0047eea8)
00403B58   call        @ILT+905(std::basic_ostream<char,std::char_traits<char> >::operator<<) (0040138e)
68:       cout << (*b) ;
00403B5D   mov         edx,dword ptr [ebp-34h]
00403B60   mov         eax,dword ptr [edx]
00403B62   push        eax
00403B63   mov         ecx,offset std::cout (0047eea8)
00403B68   call        @ILT+905(std::basic_ostream<char,std::char_traits<char> >::operator<<) (0040138e)

从上述汇编代码中可以看出,在对const常量进行读取时,编译器会直接用const常量对应的立即数直接替换,而不去访问内存,所以即使通过指针对const常量的内存进行了修改,也不会影响const常量输出的值。

   如果我们把const int a=2;改为int i=2; const int a=c,则通过*b修改了a的内存空间后,会导致a的值的改变。因为在这种情况下,给a赋值的是变量i,而不是一个整型字面值常量。

 

  •  
    houliang120 2014.09.28
     
    很遗憾,您给出的程序的答案不对,还是给出汇编代码的那一楼才是真相,const变量它有自己的内存,同时更重要的是const变量在编译的时候全部被替换掉了(直接在操作数里面就换掉了,所以可以认为和它本身的内存已经不同步了)
     

另:静态static,常量const,详细探究

全部评论

·