C++const/常量折叠

来源:互联网 发布:淘宝上的十字绣靠谱吗 编辑:程序博客网 时间:2024/04/30 19:36

常量概念:为了使程序员能够在变和不变之间画一条界限,这在C++程序设计中提供了安全性和可控性。

const的最初动机是取代预处理器#define来进行值替代。

宏(#define)

预编译器可以不受限制的建立宏并用它来代替值。预编译器只做文本替代,它没有类型检查概念,也没有类型检查功能

宏定义只在预处理期间存在,因此它不占用存储空间且能放在一个头文件里,目的是在使用它的所有编译单元提供一个值。


const

const默认为内部连接,也就是说,const仅在const被定义过的文件里才是可见的,而在连接时不能被其他编译单元看到。当定义一个const时,必须赋一个值给它,除非用extern作出了清楚的说明。

eg:extern const int bufsize;

通常情况下,当extern不是定义的一部分,不会分配存储空间。如果用const,那么编译时会进行常量折叠。

常量折叠:编译器在编译时可以通过必要的计算吧一个复杂的常量表达式通过缩减简单化。

const创建储存空间:(1):复杂的结构;(2):强制声明为extern的符号常量;(3):取符号常量的地址等操作

由于extern意味着使用外部连接,因此必须分配储存空间,这也就是说有几个不同的编译单元应当能够引用它,所以它必须有存储空间。

以上主要参考《C++编程思想》

----------------------------------------------------------------------------------------------------------------------------------------------

通过验证发现了一些问题

#include "stdafx.h"#include<iostream>using namespace std;int main(){    int dd = 2;    const int n = 9;    int *p = (int*)&n;    *p = 1;    cout << &n << " " << n << endl;    cout<< p << " " << *p << endl;    cout << *(&n) <<" "<< *(&n + 1 - 1) << endl;    int tp = 5;    cout << *(&tp) << " " << *(&tp + 1 - 1) << endl;    return 0;}

结果:


与预期不一致

1.地址相同,内容不一样

2.通过指针修改const n

3.*(&n)与*(&n+1-1)值为何不一样

注:const int n 不是编译期间的const,推测n不是储存在常量储存区,所以常量n值是可以改变的。若extern const int n = 9;则n定义在常量储存区n值是不能改变的,见:下面例2。(暂时没有看到有明确说明的资料)


看一下汇编

int main(){001E2500  push        ebp  001E2501  mov         ebp,esp  001E2503  sub         esp,0F4h  001E2509  push        ebx  001E250A  push        esi  001E250B  push        edi  001E250C  lea         edi,[ebp-0F4h]  001E2512  mov         ecx,3Dh  001E2517  mov         eax,0CCCCCCCCh  001E251C  rep stos    dword ptr es:[edi]  001E251E  mov         eax,dword ptr ds:[001EB008h]  001E2523  xor         eax,ebp  001E2525  mov         dword ptr [ebp-4],eax      int dd = 2;001E2528  mov         dword ptr [dd],2      const int n = 9;001E252F  mov         dword ptr [n],9      int *p = (int*)&n;001E2536  lea         eax,[n]  001E2539  mov         dword ptr [p],eax      *p = 1;001E253C  mov         eax,dword ptr [p]      *p = 1;001E253F  mov         dword ptr [eax],1      cout << n << endl;001E2545  mov         esi,esp  001E2547  push        1E1082h  001E254C  mov         edi,esp  001E254E  push        9  //常量折叠001E2550  mov         ecx,dword ptr ds:[1EC098h]  001E2556  call        dword ptr ds:[1EC0ECh]  001E255C  cmp         edi,esp  001E255E  call        __RTC_CheckEsp (01E1159h)  001E2563  mov         ecx,eax  001E2565  call        dword ptr ds:[1EC0ACh]  001E256B  cmp         esi,esp  001E256D  call        __RTC_CheckEsp (01E1159h)      cout << *p << endl;001E2572  mov         esi,esp  001E2574  push        1E1082h  001E2579  mov         edi,esp  001E257B  mov         eax,dword ptr [p]  001E257E  mov         ecx,dword ptr [eax]  001E2580  push        ecx  001E2581  mov         ecx,dword ptr ds:[1EC098h]  001E2587  call        dword ptr ds:[1EC0ECh]  001E258D  cmp         edi,esp  001E258F  call        __RTC_CheckEsp (01E1159h)  001E2594  mov         ecx,eax  001E2596  call        dword ptr ds:[1EC0ACh]  001E259C  cmp         esi,esp  001E259E  call        __RTC_CheckEsp (01E1159h)      cout << &n << endl;001E25A3  mov         esi,esp  001E25A5  push        1E1082h      cout << &n << endl;001E25AA  mov         edi,esp  001E25AC  lea         eax,[n]  001E25AF  push        eax  001E25B0  mov         ecx,dword ptr ds:[1EC098h]  001E25B6  call        dword ptr ds:[1EC0E8h]  001E25BC  cmp         edi,esp  001E25BE  call        __RTC_CheckEsp (01E1159h)  001E25C3  mov         ecx,eax  001E25C5  call        dword ptr ds:[1EC0ACh]  001E25CB  cmp         esi,esp  001E25CD  call        __RTC_CheckEsp (01E1159h)      cout<< p << endl;001E25D2  mov         esi,esp  001E25D4  push        1E1082h  001E25D9  mov         edi,esp  001E25DB  mov         eax,dword ptr [p]  001E25DE  push        eax  001E25DF  mov         ecx,dword ptr ds:[1EC098h]  001E25E5  call        dword ptr ds:[1EC0E8h]  001E25EB  cmp         edi,esp  001E25ED  call        __RTC_CheckEsp (01E1159h)  001E25F2  mov         ecx,eax  001E25F4  call        dword ptr ds:[1EC0ACh]  001E25FA  cmp         esi,esp  001E25FC  call        __RTC_CheckEsp (01E1159h)      cout << *(&n) <<" "<< *(&n + 1 - 1) << endl;001E2601  mov         esi,esp  001E2603  push        1E1082h  001E2608  mov         edi,esp  001E260A  mov         eax,dword ptr [n]      cout << *(&n) <<" "<< *(&n + 1 - 1) << endl;001E260D  push        eax  001E260E  push        1E8B30h  001E2613  mov         ebx,esp  001E2615  push        9  //常量折叠001E2617  mov         ecx,dword ptr ds:[1EC098h]  001E261D  call        dword ptr ds:[1EC0ECh]  001E2623  cmp         ebx,esp  001E2625  call        __RTC_CheckEsp (01E1159h)  001E262A  push        eax  001E262B  call        std::operator<<<std::char_traits<char> > (01E141Fh)  001E2630  add         esp,8  001E2633  mov         ecx,eax  001E2635  call        dword ptr ds:[1EC0ECh]  001E263B  cmp         edi,esp  001E263D  call        __RTC_CheckEsp (01E1159h)  001E2642  mov         ecx,eax  001E2644  call        dword ptr ds:[1EC0ACh]  001E264A  cmp         esi,esp  001E264C  call        __RTC_CheckEsp (01E1159h)      int tp = 5;001E2651  mov         dword ptr [tp],5      cout << *(&tp) << " " << *(&tp + 1 - 1) << endl;001E2658  mov         esi,esp  001E265A  push        1E1082h  001E265F  mov         edi,esp  001E2661  mov         eax,dword ptr [tp]  001E2664  push        eax  001E2665  push        1E8B30h  001E266A  mov         ebx,esp  001E266C  mov         ecx,dword ptr [tp]  001E266F  push        ecx  001E2670  mov         ecx,dword ptr ds:[1EC098h]  001E2676  call        dword ptr ds:[1EC0ECh]  001E267C  cmp         ebx,esp  001E267E  call        __RTC_CheckEsp (01E1159h)  001E2683  push        eax  001E2684  call        std::operator<<<std::char_traits<char> > (01E141Fh)  001E2689  add         esp,8  001E268C  mov         ecx,eax  001E268E  call        dword ptr ds:[1EC0ECh]  001E2694  cmp         edi,esp  001E2696  call        __RTC_CheckEsp (01E1159h)  001E269B  mov         ecx,eax  001E269D  call        dword ptr ds:[1EC0ACh]  001E26A3  cmp         esi,esp  001E26A5  call        __RTC_CheckEsp (01E1159h)      return 0;001E26AA  xor         eax,eax  }

从反汇编可以看出

1.vs2015编译器为const创建的储存空间

2.int *p = (int*)&n;  *p = 1; 指针p改变了常量储存空间储存的值

3.在打印的时候,cout<<n<<endl; 编译器将n进行了常量折叠

4.打印*(&n)值时,进行了常量折叠,打印出的值是常量n = 9的值;*(&n + 1 - 1)为进行常量折叠,打印的事n储存空间的值

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

------------------------------------------------------------------------------------------------------------------------------------

以下折抄自《C++编译思想》第八章 常量

C++中的const默认为内部连接,所以不能在一个文件中定义一个const,而在另一个文件中又把它作为extern来引用。为了使const成为外部连接以便让另外一个文件可以对它引用,必须明确地把它定义成exteren,如下面这样:

extern const int x = 1;

注意,通过对它进行初始化并指定为extern,我们强迫给它分配内存(虽然编译器在这里仍然可以选择常量折叠)。初始化使它成为了一个定义而不是声明。在C++中的声明:

extern const int x;

意味着在别处进行了定义。现在明白了为什么C++要求一个const定义时需要初始化:初始化把定义和声明区别开来。当进行了extern const声明时,编译器就不能够进行常量折叠了 ,因为它不知道具体的值。

个人理解:当定义的const为全局变量时,为了使它成为外部连接,需要把它定义成extern,系统会为它分配内存,在定义的文件中可能会进行常量折叠,别的文件不会进行常量折叠。

例2:

//consttest.cppvoid f1();extern const int gnum = 8;int main(){    int pp = gnum;    const int *p = &gnum;    f1();    cout << "end main " << &gnum << endl;}

//constT1.cppextern const int gnum;void f1(){    const int *p = &gnum;    int n = gnum;    cout << "f1" << &gnum << endl;}

看一下反汇编

//consttest.cpp


//constT1.cpp



当前实例中,gnum储存在常量储存区,在gnum定义的文件(consttest.cpp)中gnum进行了常量折叠,在另外文件(constT1.cpp)中使用的是地址里面的内容,地址赋值要求常量储存区需要是指向const的指针,或是引用则需要是const引用(个人见解)

个人笔记,学术不精,有一些尚未理解透彻,学无止境!

原创粉丝点击