编译器对const常量的处理
September 25, 2010 Post in 程序设计
先来看一段代码,这是今天师兄贴给的。
1 2 3 4 5 6 7 8 9 | #include <stdio.h> int main() { const int i = 10; int* pi = const_cast<int*>(&i); *pi = 100; printf("%d %d\n", i, *pi); return 0; } |
代码运行的结果是什么呢?按道理讲,i是一个const类型的常量,pi指向了存储i的内存空间的地址,然后通过pi改变了内存中的这个值,那么,结果应该是“100 100”?
可以自己跑了这段代码之后发现,输出的结果是“10 100”,这是为什么呢?
很明显,在printf输出的时候,i的值依然为最初的10,而不是改变之后的值。难道pi没有改变内存中存储的数值?于是按如下方法修改代码,在使用pi赋值以后,再用i的地址取值。
1 2 3 4 5 6 7 8 9 10 | #include <stdio.h> int main() { const int i = 10; int* pi = const_cast<int*>(&i); *pi = 100; int* pi2 = const_cast<int*>(&i); printf("%d %d %d\n", i, *pi, *pi2); return 0; } |
这样得到的结果是“10 100 100”。也就是说,*pi = 100执行正确,也确实改变了内存中的值。但是printf输出的i依然是10,那么就可以断定,问题出在编译器的环节。解决这种问题,最简单的方法就是看一下编译器生成汇编。直接 g++ -S 一下得到第一段程序代码的汇编如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | .file "c.cpp" .def ___main; .scl 2; .type 32; .endef .section .rdata,"dr" LC0: .ascii "%d %d\12\0" .text .globl _main .def _main; .scl 2; .type 32; .endef _main: LFB8: pushl %ebp LCFI0: movl %esp, %ebp LCFI1: andl $-16, %esp LCFI2: subl $32, %esp LCFI3: call ___main movl $10, 24(%esp) leal 24(%esp), %eax movl %eax, 28(%esp) movl 28(%esp), %eax movl $100, (%eax) movl 28(%esp), %eax movl (%eax), %eax movl %eax, 8(%esp) movl $10, 4(%esp) movl $LC0, (%esp) call _printf movl $0, %eax leave ret LFE8: .def _printf; .scl 2; .type 32; .endef |
可以看到,第25到30行是调用printf函数。其中第25、26、27三行是取出pi指向的空间的数值放入堆栈,第28行是将数字10放入堆栈。看到这里就知道为什么printf会输出10了。在代码中取i值的地方,在编译的时候,编译器直接将其替换为i指向的常量的值。注意,这是在编译时替换的,也就是说跟内存中那个常量的位置存的是什么数值没有关系。而const_cast则是让编译器把常量的标识符当作变量对待。
不过正常人写代码一般是不会遇到这种问题的,常量就该尽职的做常量,不要变来变去的,要不直接用变量就好了~不过还是要谨记,防止出现不必要的麻烦,要不出了问题还真不好解决…
New Comments