C++中如何修改const變量你知道嗎
一、結(jié)論
聲明:不同于C語(yǔ)言的const變量修改問(wèn)題(可以通過(guò)指針間接修改const變量的值),這里只討論C++ 里的const。
C++ const 修飾符,表示常量,即如果以后保證不會(huì)修改則聲明為const,否則若要修改,那一開始為什么還要聲明為const呢?
根據(jù)C++標(biāo)準(zhǔn),對(duì)于修改const變量,屬于:未定義行為(指行為不可預(yù)測(cè)的計(jì)算機(jī)代碼),這樣一來(lái)此行為取決于各種編譯器的具體實(shí)現(xiàn)(即不同編譯器可能表現(xiàn)不同)。
故結(jié)論就是:不建議這么做!
但是,是的,但是,網(wǎng)上論壇、博客里均有有關(guān)如何修改const變量的方法,其不是依賴于某種具體的編譯器,就是講的欠考慮。
方法是在定義變量的時(shí)候加上volatile關(guān)鍵字(沒(méi)有其他方法了嗎(比如,const_cast ...)? 是的,目前為止,我只知道這種方法是可能的):
const volatile int i = 10;
注:關(guān)于volatile這里不細(xì)講,詳見:volatile 關(guān)鍵字??紤]到volatile的重要性,后面自己也會(huì)寫一篇關(guān)于volatile詳解的文章。
二、分析
為了說(shuō)明問(wèn)題,下面在三種編譯器環(huán)境下做幾個(gè)小實(shí)驗(yàn)
1. g++
#include <stdio.h>
int main()
{
const volatile int i = 10;
int* pi = (int*)(&i);
*pi = 100;
printf("*pi: %d\n",*pi);
printf("i: %d\n",i);
printf("pi: %p\n",pi);
printf("&i: %p\n", &i);
return 0;
}輸出結(jié)果:

gdb查看其匯編代碼(命令:進(jìn)入gdb,然后輸入:disass main):

可以看出:輸入*pi 和 i 時(shí)均是從堆棧(即內(nèi)存)中取數(shù)的。
反例:把 volatile關(guān)鍵字去掉:
#include <stdio.h>
int main()
{
const int i = 10;
int* pi = (int*)(&i);
*pi = 100;
printf("*pi: %d\n",*pi);
printf("i: %d\n",i);
printf("pi: %p\n",pi);
printf("&i: %p\n", &i);
return 0;
}輸出結(jié)果:

由此可見:在沒(méi)有volatile關(guān)鍵字修飾時(shí),const 變量 i 的值時(shí)沒(méi)有改變的。
運(yùn)用gdb查看其匯編代碼:

注意此時(shí)(沒(méi)有加volatile修飾符),輸出 變量 i 的值時(shí)直接將 0xa(10)值(從符號(hào)表中取出的)輸出,即此處編譯器進(jìn)行了優(yōu)化,沒(méi)有從內(nèi)存中讀。
注意到:指針 pi 和 &i(i 的地址)值卻是一樣的。So ,Why?
這就是C++中的常量折疊:指const變量(即常量)值放在編譯器的符號(hào)表中,計(jì)算時(shí)編譯器直接從表中取值,省去了訪問(wèn)內(nèi)存的時(shí)間,從而達(dá)到了優(yōu)化。
而在此基礎(chǔ)上加上volatile修改符,即告訴編譯器該變量屬于易變的,不要對(duì)此句進(jìn)行優(yōu)化,每次計(jì)算時(shí)要去內(nèi)存中取數(shù)。
這里也有個(gè)小細(xì)節(jié):每種編譯器對(duì)volatile修飾符的修飾作用效果不一致,有的就直接“不理會(huì)”,如VC++6.0編譯器(下面會(huì)講到)。
2. dev c++
運(yùn)行結(jié)果與1(g++)一致。
3. VC++ 6.0
(1)添加volatile修飾符時(shí),輸出結(jié)果(程序代碼同上):

i 的值還是10,沒(méi)有改變!這是為什么呢?不急,先看下其匯編代碼:
注意:g++ 匯編代碼的mov指令 與 VC++ 6.0的mov指令不同(傳送方向相反)。

真相大白:雖然定義const變量的同時(shí)加上了volatile修飾符,但VC++ 6.0編譯器還是進(jìn)行了優(yōu)化措施,輸出 i 時(shí) 從編譯器的符號(hào)表中取值,直接輸出。
(2)無(wú) volatile 修飾符時(shí)。輸出結(jié)果:

i 的值沒(méi)有改變,預(yù)期中。其匯編代碼為:

結(jié)果與添加volatile時(shí)相同。
即在VC++6.0編譯環(huán)境下,在const變量定義時(shí)添加volatile修飾符,與不添加效果是一樣的。編譯器都采取了優(yōu)化(甚至把編譯器優(yōu)化選項(xiàng)關(guān)閉還是如此,有點(diǎn)恐怖...)。
4. VS 2010
再看下Microsoft編譯器家族的高級(jí)版本:
(1)添加 volatile 修飾符時(shí),輸出結(jié)果:

i 的值被成功修改了!
(2)無(wú) volatile 修飾符時(shí),輸出結(jié)果:

i 的值沒(méi)有被修改。
故:不建議修改const變量的值,即使修改也要熟悉當(dāng)前使用的編譯器對(duì)于該 未定義行為 是如何解釋的。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
VisualStudio2022下配置 OpenMP多線程編程環(huán)境與運(yùn)行
本文主要介紹了VisualStudio2022下配置 OpenMP多線程編程環(huán)境與運(yùn)行,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
詳解C/C++高精度算法的簡(jiǎn)單實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了C/C++中高精度算法(加減乘除)的簡(jiǎn)單實(shí)現(xiàn),方便以后需要時(shí)拷貝使用。感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12
C語(yǔ)言線程對(duì)象和線程存儲(chǔ)的實(shí)現(xiàn)
這篇文章主要介紹了C語(yǔ)言線程對(duì)象和線程存儲(chǔ)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
C語(yǔ)言程序設(shè)計(jì)之指針的應(yīng)用詳解
為了讓大家能夠更準(zhǔn)確的了解C語(yǔ)言中指針的使用,本文為大家準(zhǔn)備了四個(gè)指針相關(guān)的例題,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以學(xué)習(xí)一下2022-11-11
C語(yǔ)言實(shí)現(xiàn)萬(wàn)年歷小功能
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)萬(wàn)年歷小功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03
c++將vector迭代器轉(zhuǎn)換為指針的實(shí)現(xiàn)方式
這篇文章主要介紹了c++將vector迭代器轉(zhuǎn)換為指針的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11

