c++中const常量的存储位置探讨

时间:2022-08-16 21:47:57

首先看一段诡异的代码:

#include <stdio.h>

int main()
{
const float intValue=2.2;
float *j=(float *)&intValue;
*j=1.1;

printf("intValue address:0x%x\n",&intValue);
printf("j address:0x%x\n",j);
printf("j:%f\n",*j);
printf("intValue:%f\n",intValue);

return 0;
}
输出结果如下:
intValue address:bfd8dddcj address:bfd8dddcj:1.100000intValue:2.200000
这就纳闷了,为什么地址一样,而所指的值不一样呢~~这就要探讨一下编译器在处理const常量方面的一些机制了,以上代码生成的汇编如下:
.file	"constASM.cpp"	.section	.rodata //rodata区,此处保存printf中的一些常量.LC2:	.string	"intValue address:0x%x\n".LC3:	.string	"j address:0x%x\n".LC4:	.string	"j:%f\n".LC6:	.string	"intValue:%f\n"	.text.globl main	.type	main, @functionmain:.LFB2:	leal	4(%esp), %ecx      //esp:Stack Pointer, 堆栈指针,指向堆栈中即将被操作的那个地址.LCFI0:	andl	$-16, %esp	pushl	-4(%ecx).LCFI1:	pushl	%ebp               //ebp可以理解为保存指针的寄存器.LCFI2:	movl	%esp, %ebp        .LCFI3:	pushl	%ecx.LCFI4:	subl	$36, %esp         //esp自减36,相当于堆栈的大小定为36.LCFI5:	movl	$0x400ccccd, %eax //&intValue操作,会分配内存,并没有引用.LC5,而是直接用立即数赋值	movl	%eax, -12(%ebp)   //为(ebp-12)地址赋值0x400ccccd,0x400ccccd,即为浮点数2.2	leal	-12(%ebp), %eax   	movl	%eax, -8(%ebp)    //此时(ebp-8)地址赋值为0x400ccccd	movl	-8(%ebp), %edx    //将(ebp-8)地址存入edx	movl	$0x3f8ccccd, %eax 	movl	%eax, (%edx)      //为(ebp-8)地址赋值为0x3f8ccccd,即浮点数1.1	leal	-12(%ebp), %eax   	movl	%eax, 4(%esp)     //将(ebp-12)地址的值读入(4+esp)地址为printf做准备	movl	$.LC2, (%esp)     //将.LC2的地址读入(esp)地址,为printf做准备	call	printf            //printf("intValue address:0x%x\n",&intValue);	movl	-8(%ebp), %eax	movl	%eax, 4(%esp)	movl	$.LC3, (%esp)	call	printf            //printf("j address:0x%x\n",j);	movl	-8(%ebp), %eax	flds	(%eax)            //浮点数压栈,压入的是(ebp-8)地址的值	fstpl	4(%esp)	movl	$.LC4, (%esp)	call	printf            //printf("j:%f\n",*j);	fldl	.LC5              //浮点数压栈,压入的是LC5,这里就是区别!!!直接从符号表中找到intValue,跟前面的内存操作无关!!	fstpl	4(%esp)           //	movl	$.LC6, (%esp)    	call	printf            //printf("intValue:%f\n",intValue);	movl	$0, %eax	addl	$36, %esp	popl	%ecx	popl	%ebp	leal	-4(%ecx), %esp	ret.LFE2:	.size	main, .-main	.section	.rodata	.align 8.LC5:	.long	-1610612736	.long	1073846681	.section	.eh_frame,"a",@progbits.Lframe1:	.long	.LECIE1-.LSCIE1.LSCIE1:	.long	0x0	.byte	0x1..//此处省略一部分..LEFDE1:	.ident	"GCC: (GNU) 4.3.0 20080428 (Red Hat 4.3.0-8)"	.section	.note.GNU-stack,"",@progbits

从以上分析可以得到以上结论:
1.对const常量取地址时,编译器会进行内存分配,并将常量转换为立即数存入内存,而不是存入记录在常量表中的地址

2.在使用常量时,编译器回到常量表中查询对应的常量,并将其替换,这部分没有涉及内存分配,也跟曾经创建的常量的内存地址无关。