c++的常量折叠

一,前言

有关C++语言的const常量的考察点:肯定是const常量的内存不是分配在read-only的存储区的,const常量的内存分配区是很普通的栈或者全局区域。也就是说const常量只是编译器在编译的时候做检查,根本不存在什么read-only的区域

所以说C++的const常量和常量字符串是不同的,常量字符串是存储在read-only的区域的,他们的具体的存储区域是不同的。

二,常量折叠

1,举例说明

常量折叠就是将常量表达式计算求值,并用求得的值来替换表达式,放入常量表。可以算作一种编译优化。(预编译阶段

形象的例子,场景:老式的电影院,最后一排只能坐放映师,门口的检票员,一开始检票员手里没有对应表(常量/符号表),当有人进入电影院,检票员会将其姓名喝座位号写入对应表里,检票员不允许观众乱坐位置,每个人都必须坐在自己票上的位子。电影院里的每个观众席都是const常量类型,检票员就是编译器。

类似c++中,对于那些const变量,在预编译阶段就决定了他们在内存的存储位置,你如果直接改这个常量的值是不合法的。

但有个问题,观众进到电影院以后,我跟旁边的哥们说一声,我给你100块,你给我换一下位置吧,如果他同意了,我们就能换。检票员管不着,进都进来了,还能怎样。所以两个人就调换了位子。

同样,c++的const变量,编译器在编译的时候会进行语义检查,查看符号表,如果修改const的值会报错。但是绕过编译,在允许的时候修改const就会成功。

对于常量字符串的存储位置就不同了,它的存储位置是read-only的区域,就像电影院最后的那个放映师的位置,那里是一个特殊的位置,是真的不允许随便的调换。

2,理解

先说一个错误的理解:可折叠的常量就像宏一样,在预编译阶段对const常量的引用一律被替换为常量所对应的值。和普通的宏替换没有什么区别,并且编译器不会为该变量分配存储空间。

正确的理解是:常量确实会像宏替换一样,把对常量的引用替换为常量对应的值,但是该常量是分配空间的,并且考编译器来检查常量属性。

#define PI 3.14

 

int main(){

    const int r = 10;

    int p = PI;  //这里发生宏替换,在预编译时替换

    int len = 2*r; //这里发生常量折叠,常量r的引用会替换成对应的值,相当于len=2*10;

} 

编译器会把const 常量放入到符号表中,同时,会为该变量分配空间,栈空间或者全局空间。既然放到了符号表中,就意味着可以找到这个变量的地址。

符号表不是一张表,是一系列表的统称,这里的const常量,会把这个常量的名字、类型、内存地址、值都放到常量表中。符号表还有一个变量表,这个表放变量的名字、类型、内存地址,但是没有放变量的值。

接下来我们通过一段代码来分析一下了解一下常量折叠及其特性:

int main()

{

    const int a = 10;

    int b = 2*a;          //此处预编译的时候发生常量折叠,相当于b=2*10

    int c = b;

    int d = *(&a);        //此处的*(&a),一般编译器在预编译时会对其优化成a。

    int *p = (int*)&a;    //此处对a取值操作,常量a是有内存的。    

    *p = 100;

    printf("0x%p\n0x%p\n%d\n%d\n",&a,p,a,*p);  //此处预编译的时候发生常量折叠,相当于

                                               //printf("0x%p\n0x%p\n%d\n%d\n",&a,p,a,*p);

    return 0;

}

1 编译器会为该常量在栈上分配一块空间。

const int a = 10;

0099446E  mov         dword ptr [a],0Ah  

a是一个常量,按理说常量是存储在代码段的,可通过反汇编可以清楚地看到编译器确实为a在栈上分配了一块空间。

在这一点上const常量和宏是有区别的,宏在预编译阶段就完成了替换,编译器并不会为宏分配空间。

2 代码中遇上a的地方,直接替换为a的值

下面我们来比较一下常量折叠和正常变量的赋值

   int b = 2*a;//常量折叠赋值

00994475  mov     dword ptr [b],14h//真正的实现形式是int b=2*10  

    int c = b;//正常变量赋值

0099447C  mov     eax,dword ptr [b]//借助寄存器eax把b的值传递给c  

0099447F  mov     dword ptr [c],eax

3指针p指向a在栈上的空间,而且明明通过*p=100对该空间上的内容作出了改变,为什么输出的a依旧是10呢?

话不多说,我们来看反汇编:

int *p = (int*)&a;

00994482  lea         eax,[a]  //通过寄存器eax把a的地址给指针p

00994485  mov         dword ptr [p],eax  

    *p = 100;

00994488  mov         eax,dword ptr [p]  

0099448B  mov         dword ptr [eax],64h//修改p指向的空间的内容  

最后说一下gcc编译的C语言的const常量,这里并没有做常量折叠的这种优化,类似于const常量前面加上volatile这个关键字。

const volatile int a = 10;

 

 

from:https://blog.csdn.net/u011749929/article/details/107006922

posted @ 2023-06-14 21:50  imxiangzi  阅读(62)  评论(0编辑  收藏  举报