C++常量折叠

转自http://blog.csdn.net/yby4769250/article/details/7359278,作者:yby4769250


今天回顾了大学这几年学习C++的点点滴滴,在回顾了“常量折叠”这里时,突然困惑了,当时学习这点知识时的理解是:可折叠的常量像宏一样,在预编译阶段对常量的引用一律被替换为常量所对应的值,就和普通的宏替换没什么区别,并且,编译器不会为该常量分配空间。现在回顾起来,当时是多么的天真,被现象迷惑了,常量折叠确实会像宏一样把对常量的引用替换为常量对应的值,但是,并非不给该常量分配空间,如下代码:

[cpp]  view plain copy
  1. #define PI 3.14  
  2. int main()  
  3. {  
  4.     const int r = 10;  
  5.   
  6.     int p = pI; //这里会在预编译阶段产生宏替换,PI直接替换为3.14,其实就是int p = 3.14;  
  7.     int len = 2*r; //这里会发生常量折叠,也就是对常量r的引用会替换成他对应的值,相当于int len = 2*10;  
  8.     return 0;  
  9. }  

如上述代码中所述,常量折叠表面上的效果和宏替换是一样的,只是,“效果上是一样的”,而两者真正的区别在于,宏是字符常量,在预编译完宏替换完成后,该宏名字会消失,所有对宏如PI的引用已经全部被替换为它所对应的值,编译器当然没有必要再维护这个符号。而常量折叠发生的情况是,对常量的引用全部替换为该常量如r的值,但是,常量名r并不会消失,编译器会把他放入到符号表中,同时,会为该变量分配空间,栈空间或者全局空间。

为了能更清楚的体现出常量折叠,下面做几个对照实验,看代码和输出便了然:

[cpp]  view plain copy
  1. int main()  
  2. {  
  3.     int i0 = 11;  
  4.   
  5.     const int i=0;         //定义常量i  
  6.     int *j = (int *) &i;   //看到这里能对i进行取值,判断i必然后自己的内存空间  
  7.     *j=1;                  //对j指向的内存进行修改  
  8.         printf("%d\n%d\n%d\n%d\n",&i,j,i,*j); //观看实验效果  
  9.     const int ck = 9;     //这个对照实验是为了观察,对常量ck的引用时,会产生的效果  
  10.     int ik = ck;  
  11.   
  12.     int i1 = 5;           //这个对照实验是为了区别,对常量和变量的引用有什么区别  
  13.     int i2 = i1;  
  14.       
  15.   
  16.     return 0;  
  17.   
  18. }  

上面的代码会输出:

0012ff7c
0012ff7c

0

1

这能说明什么,至少能说明两点:

1、ij地址相同,指向同一块空间,i虽然是可折叠常量,但是,i确实有自己的空间

2、ij指向同一块内存,但是*j = 1对内存进行修改后,按道理来说,*j==1,i也应该等于1,而实验结果确实i实实在在的等于0,这是为什么呢,就是本文所说的内容,i是可折叠常量,在编译阶段对i的引用已经别替换为i的值了,也就是说

[cpp]  view plain copy
  1. printf("%d\n%d\n%d\n%d\n",&i,j,i,*j)  
中的i已经被替换,其实已经被改为

[cpp]  view plain copy
  1. printf("%d\n%d\n%d\n%d\n",&i,j,0,*j)  
[cpp]  view plain copy
  1.   
为了使实验更具说服力,直接上汇编代码,比较实验的不同点:

[plain]  view plain copy
  1. 4:    int main()  
  2. 5:    {  
  3. 00401030   push        ebp  
  4. 00401031   mov         ebp,esp  
  5. 00401033   sub         esp,5Ch  
  6. 00401036   push        ebx  
  7. 00401037   push        esi  
  8. 00401038   push        edi  
  9. 00401039   lea         edi,[ebp-5Ch]  
  10. 0040103C   mov         ecx,17h  
  11. 00401041   mov         eax,0CCCCCCCCh  
  12. 00401046   rep stos    dword ptr [edi]  
  13. 6:        int i0 = 11;  
  14. 00401048   mov         dword ptr [ebp-4],0Bh  
  15. 7:  
  16. 8:        const int i=0;  
  17. 0040104F   mov         dword ptr [ebp-8],0 //睁大眼睛,编译器确实为常量i分配了栈空间,并赋值为0  
  18. 9:        int *j = (int *) &i;  
  19. 00401056   lea         eax,[ebp-8]  
  20. 00401059   mov         dword ptr [ebp-0Ch],eax  
  21. 10:       *j=1;  
  22. 0040105C   mov         ecx,dword ptr [ebp-0Ch]  
  23. 0040105F   mov         dword ptr [ecx],1  
  24. 11:                                            //再看看下面的对比实验,看出对常量的引用和变量的引用的区别  
  25. 12:       const int ck = 9;  
  26. 00401065   mov         dword ptr [ebp-10h],9   //为常量分配栈空间   
  27. 13:       int ik = ck;  
  28. 0040106C   mov         dword ptr [ebp-14h],9   //看到否,对常量ck的引用,会直接替换为常量的值9,再看下面的实验  
  29. 14:  
  30. 15:       int i1 = 5;  
  31. 00401073   mov         dword ptr [ebp-18h],5  
  32. 16:       int i2 = i1;                         //这里引用变量i1,对i2进行赋值,然后看到否,对常量i1引用没有替换成i1的值,而是去栈中先取出i1的值,到edx寄存器中,然后再把值mov到i2所在的内存中  
  33. 0040107A   mov         edx,dword ptr [ebp-18h]  
  34. 0040107D   mov         dword ptr [ebp-1Ch],edx  
  35. 17:  
  36. 18:  
  37. 19:       return 0;  
  38. 00401080   xor         eax,eax  
  39. 20:  
  40. 21:   }  

通过上述实验的分析可以容易看出,对可折叠的常量的引用会被替换为该常量的值,而对变量的引用就需要访问变量的内存。


总结:常量折叠说的是,在编译阶段,对该变量进行值替换,同时,该常量拥有自己的内存空间,并非像宏定义一样不分配空间,需澄清这点


温故而知新,每一次都有不一样的收获


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值