const靈異現(xiàn)象
版本:0.1
最后修改:2010-11-22
撰寫:李現(xiàn)民
概述
const在c++中意味著“不可改變”,但在有些情況下我們可以“合法”地繞過編譯器去修改一些const數(shù)據(jù),比如const_cast就可以剝離一個(gè)對(duì)象的const屬性。然而,我們這樣做在多大程度上是“合理”的,卻因不同的問題而論,也許一不小心,你就可能掉入陷阱之中。
以下問題,我只分析,不說(shuō)話,請(qǐng)各位看官自己判斷。
當(dāng)目標(biāo)是一個(gè)常數(shù)
這件事源于在網(wǎng)上看到的一篇文章,其來(lái)源已經(jīng)不可考,但大意是:就如下C++程序,其輸出是什么:
void
foo()
{
const
int
a =
1;
int*
p =
const_cast<int*>(&a);
*p =
2;
printf("
a= %d\n *p= %d\n &a= %x\n p= %x \n\n",
a,
*p,
&a,
p);
}
我在VC2008下的實(shí)測(cè)結(jié)果為:
a = 1
*p = 2
&a = 12ff6c
p = 12ff6c
好了,問題出現(xiàn):明明p所指向的就是變量a,但為何打印其值時(shí)a!=*p?
這并非是我用錯(cuò)了const_cast,也不是編譯器進(jìn)行了優(yōu)化的問題。事實(shí)上,在各版本的VC與g++下的運(yùn)行結(jié)果均是如此。
以下是VC2008下debug版本的匯編代碼:
const
int a = 1;
0041146E
mov dword ptr [a],1
int*
p = const_cast<int*>(&a);
00411475
lea eax,[a]
00411478
mov dword ptr [p],eax
*p =
2;
0041147B
mov eax,dword ptr [p]
0041147E
mov dword ptr [eax],2
printf("
a= %d\n *p= %d\n &a= %x\n p= %x \n\n", a, *p, &a, p);
00411484
mov esi,esp
00411486
mov eax,dword ptr [p]
00411489
push eax
0041148A
lea ecx,[a]
0041148D
push ecx
0041148E
mov edx,dword ptr [p]
00411491
mov eax,dword ptr [edx]
00411493
push eax
00411494
push 1
00411496
push offset string " a=\t%d\n *p=\t%d\n &a=\t%x\n
p=\t%x \n\n"... (415808h)
0041149B
call dword ptr [__imp__printf (419318h)]
從printf()的四個(gè)參數(shù)入棧過程中我們可以看出:指針p的確指向變量a了,而變量a處的數(shù)值也的確被改寫成2了,問題是:當(dāng)壓入a的值的時(shí)候,編譯器直接壓入了其原始數(shù)值1。
關(guān)鍵其實(shí)在于:const_cast所操作的目標(biāo)是否為基礎(chǔ)數(shù)據(jù)類型(char,
int, float, double等),如果是類(或結(jié)構(gòu)體)對(duì)象則又將是另一番情形。
當(dāng)修改字符串常量
這個(gè)問題最早見于一篇文章《Solmyr的小品文系列之一:字符串放在哪里?》,在這里我只不過轉(zhuǎn)述一二。
代碼如下:
void
foo()
{
char*
str1 =
"watch";
const
char*
str2 =
"watch";
char
str3[] =
"watch";
str1[0] =
'm';
std::cout<<
str1
<< std::endl
<< str2
<< std::endl
<< str3
<< std::endl;
}
VC6中Release版本運(yùn)行結(jié)果如下:
match
match
watch
VC2008中Release版本運(yùn)行結(jié)果如下:
watch
watch
watch
容易看出:這段代碼的運(yùn)行結(jié)果決定于編譯器,因?yàn)槲覀兏膶懥瞬粦?yīng)該被改寫的常量數(shù)據(jù)。更根本的原因是:由于編譯器優(yōu)化,str1與str2實(shí)際上指向的是同一份”watch”字符串。
這還帶出了另一件事:盡管str1的聲明中不帶const,但它所指向的字符串?dāng)?shù)據(jù)隱含是const類型的。
注意:這段代碼只有Release版本才能順利執(zhí)行,Debug版版本運(yùn)行時(shí)會(huì)得到一個(gè)Access
violation 。