我們課本上講的,我有點不太明白……


因為嚴格意義上的常量是一種「等效承諾」,只要結果一致,一個常量表達式可以翻譯成任何東西,它和內存沒有映射關係,所以正常來說在內存裏是沒有對應地址的。

而表達式則是一種還沒有完成的邏輯,取決於表達式的返回值,如果返回值是變數,那麼其實是可以取地址的,如果是常量,則取地址是無法完成的。

常量的問題比較複雜,放在前面說

-----

直接看程序和指令吧

const int i = 10;
int j = 20;

int main() {
int x = i;
int y = j;
}

編譯後的彙編(由G++編譯的ATT彙編):

全局變數 j 被標記好地址(_j)放在內存裏(紅框)

然後 y = j 的表達式當中實際指令(紅色箭頭),內存塊指針(RIP/EIP寄存器)中取 _j 標記的地址,賦值給ecx寄存器(也就是y變數當前代表的寄存器)

再看常量i……

賦值 x = i 簡單粗暴的指令(藍色箭頭),立即數 $10 寫入x在棧裏的內存(rbp)-&> (-4)

不熟悉彙編的話可以不用在一寄存器的這些細節,關鍵問題就是:上面的指令來看,編譯器壓根就沒給常量i分配內存,也就沒有地址這個概念

----

常量對編譯器而言,是一種特殊的注釋,他告訴編譯器:只要是等效(等值)的方式,這個表達式可以用任何形式表達,不拘泥於內存、寄存器或是指令,可以和任何其他編譯邏輯整合在一起,不用關心相互作用問題,編譯器可以用更多的手段去優化代碼。

對於語言本身而言,常量代表著這個表達式是孤立的,無狀態且具有原子性的;這樣的情況下,常量引用即複製,不需要生命週期管理,不需要考慮出上下文關係

所以……取地址這個操作打破了上面所需要的承諾

對於編譯器,常量如果能夠取地址,就必須不斷追蹤這個常量的內存地址,必須在不同的物件之間做鏈接,這樣的常量各項操作和優化都和變數沒區別。

對於語言本身,常量如果能夠取地址,就必然存在生命週期維護,必須對逃逸進行檢測,必須在代碼塊之間處理共享問題,必須對常量的引用追根溯源,這樣的常量設計上沒有意義,起不到他本該有的功能

----

表達式的話就要看情況了:

int i = 10;

//(錯誤)常量表達式不能取地址
auto a = (i+1);
//(錯誤)常量更不能取地址了
auto b = (10);

auto j=i;
auto k=j;
//(可行)返回值是變數的話,表達式地址取地址是OK的
auto c = (*j);
auto d = (*k);


嚴格地說,是取地址運算符不能作用於常量字面量上。因為從翻譯後的二進位的角度來講,這些直接書寫在代碼裏的常量字面量是由硬體直接產生的,是不具有地址屬性的。例如下面的對 1 對 a 取地址就是非法的:

const int * pi = 1;
const char * pc = a;

但是 C/C++ 裏平常說的常量一般指的是常變數,即經 const 關鍵字修飾的變數。const 變數經初始化賦值後不可再修改。但是從二進位的角度來講,它與變數無任何區別,同樣是存儲在內存中,因此可以取地址。const 的屬性只是編譯器層面在編譯時候的檢查與保護而已。例如,下面的代碼就是合法的:

int i;
scanf("%d", i);
const int ci = i;
const int * pi = ci;

char c;
scanf("%c", c);
const int cc = c;
const int * pc = cc;

至於表達式不能取地址我個人覺得這話也是說不清道不明的。表達式確實是不存在取地址的概念。但不代表表達式的運算結果不能取地址。

int a[5] = {0, 1, 2, 3, 4};
int * p = (*(a + 2));

建議你還是把你書拍個圖放上來吧。你自己說難免會使書本上原來的意思變形。


內存裡面你的程序佔用的空間被劃分為代碼區和數據區,數據區又劃分為不變數區,變數區,堆區,棧區等等。有一些常量在代碼區,如果這個所謂的常量寫在代碼裡面的話,比如

var a = b + 常量

那麼編譯系統認為在正常情況下你不可能有獲取a的地址的需求,除非你是做某些非法hack操作。我認為表達式也是這一類。還有一些常量在數據區裡面的不變數區,比如

const int a = 123

系統也不認為你有獲取這些常量地址的需求,因為這些數據是禁止修改的。


上網查下什麼叫左值和右值,你就明白了


上面那個大兄弟說的很清楚,我可以給你一個直白簡單的

就是說,內存裏放的位置不一樣,內存分很多區域,常量區代碼區棧堆等等等等,因為常量存放在內存裏的位置沒有"地址"這個概念,所以不能取地址。


說明寫書的人不懂什麼叫常量什麼叫常數,常數12在text段,是指令地址,拿到地址也是沒有意義的,所以12非法。而常量const是可以取地址的。


常量和表達式只存在於代碼中,不存在於堆、棧中。因此沒有地址


推薦閱讀:
相關文章