本文轉(zhuǎn)自徐飛翔的“const修飾符并不保證運(yùn)行時(shí)的常數(shù)性質(zhì)”
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。
const和volatile修飾符統(tǒng)稱為cv修飾符,用于指示編譯器是否允許一個(gè)程序中的某個(gè)變量的內(nèi)存是否在初始化后,仍允許被修改。其中的volatile我們已經(jīng)在前文[1]中討論過了,我們討論下const的一些特性。我們知道,cv修飾符都是給編譯器看的,用于指導(dǎo)編譯過程中的優(yōu)化(如volatile)或者用戶非法變量修改行為(如const),因此這倆個(gè)修飾符只能保證編譯時(shí)的操作合乎設(shè)計(jì)時(shí)候的需求,但是如果涉及到運(yùn)行時(shí)(running time)的一些操作,是無法保證的。我們本文討論const修飾后的變量,被運(yùn)行時(shí)『修改』的例子。
如果我們正常使用const
,理應(yīng)如下所示:
const int var = 10;
int var_b = 100;
var = 100; // 非法操作,因?yàn)関ar是const類型,初始化后不能被修改。
var_b = 10; // 合法操作,正常的賦值
然而,我們不能確保運(yùn)行時(shí),被const
修飾的變量?jī)?nèi)存,被其他程序,或者自行設(shè)計(jì)錯(cuò)誤,或者黑客行為修改,舉個(gè)例:
#include <stdio.h>
int main(void) {
int a = 10;
const int b = 9;
int c = 8;
printf("%d\t%d\t%d\n\r", a, b, c);
int* pa = &a;
const int* pb = &b;
int* pc = &c;
printf("%x\t%x\t%x\n\r", pa,pb,pc);
*(pa+1) = 100; // modify the first time
printf("%d\n\r", *(pa+1));
*(pc-1) = 300;
printf("%x\t%x\t\n\r", (pa+1), (pc-1));
printf("%d\n\r", (pa+1) == (pc-1));
printf("%d\n\r", (pa+1) == pb);
printf("%d\t%d\t%d\t%d\n\r",a,*(pa+1),c,*(pb));
return 0;
}
用g++ test_const.cpp編譯,用./a.out輸出結(jié)果,代碼輸出為:在這里插入圖片描述
我們可以發(fā)現(xiàn),通過棧[2]上相鄰的變量的地址做偏移,可以實(shí)現(xiàn)間接地對(duì)const修飾的變量進(jìn)行修改,這種行為會(huì)意料之外的修改常量,非常的危險(xiǎn),我們的代碼中需要檢查涉及到指針偏移的操作,檢查是否會(huì)出現(xiàn)越界的情況。
注意到,如果代碼中的輸出從:
printf("%d\t%d\t%d\t%d\n\r",a,*(pa+1),c,*(pb));
改成
printf("%d\t%d\t%d\t%d\n\r",a,*(pa+1),c,b);
結(jié)果是不一樣的,后者的結(jié)果仍然是輸出b = 9,那是因?yàn)镃++在編譯時(shí)對(duì)常量進(jìn)行折疊,因此直接輸出3這個(gè)立即數(shù)了,而不是從內(nèi)存里面取出常量再輸出,這一點(diǎn)要注意。Reference
[1]. https://blog.csdn.net/LoseInVain/article/details/103356324
[2]. https://blog.csdn.net/LoseInVain/article/details/103183829