1. 程式人生 > >c++中const常量的儲存位置探討

c++中const常量的儲存位置探討

首先看一段詭異的程式碼:

#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:bfd8dddc
j address:bfd8dddc
j:1.100000
intValue: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, @function
main:
.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.在使用常量時,編譯器回到常量表中查詢對應的常量,並將其替換,這部分沒有涉及記憶體分配,也跟曾經建立的常量的記憶體地址無關。