天天看點

奇怪的const

const在c++中意味着“不可改變”,但在有些情況下我們可以“合法”地繞過編譯器去修改一些const資料,比如const_cast就可以剝離一個對象的const屬性。然而,我們這樣做在多大程度上是“合理”的,卻因不同的問題而論,也許一不小心,你就可能掉入陷阱之中。

以下問題,我隻分析,不說話,請各位看官自己判斷。

當目标是一個常數

這件事源于在網上看到的一篇文章,其來源已經不可考,但大意是:就如下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下的實測結果為:

a = 1

*p = 2

&a = 12ff6c

p = 12ff6c

好了,問題出現:明明p所指向的就是變量a,但為何列印其值時a!=*p?

這并非是我用錯了const_cast,也不是編譯器進行了優化的問題。事實上,在各版本的VC與g++下的運作結果均是如此。

以下是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()的四個參數入棧過程中我們可以看出:指針p的确指向變量a了,而變量a處的數值也的确被改寫成2了,問題是:當壓入a的值的時候,編譯器直接壓入了其原始數值1。

關鍵其實在于:const_cast所操作的目标是否為基礎資料類型(char, int, float, double等),如果是類(或結構體)對象則又将是另一番情形。

當修改字元串常量

這個問題最早見于一篇文章《Solmyr的獨幕喜劇文系列之一:字元串放在哪裡?》,在這裡我隻不過轉述一二。

代碼如下:

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版本運作結果如下:

match

watch

VC2008中Release版本運作結果如下:

容易看出:這段代碼的運作結果決定于編譯器,因為我們改寫了不應該被改寫的常量資料。更根本的原因是:由于編譯器優化,str1與str2實際上指向的是同一份”watch”字元串。

繼續閱讀