const 、#define、constexpr
-
#define
define是C語言中的宏,拿來進行文本替換。如,
#define number 100
,在程序的預處理階段,會將程序中,所有出現number的地方,使用100來進行替換。缺點:這個是在預處理階段就完成的文本替換,與編譯階段是不同階段,不會進行類型檢查。
-
const
i. 編譯時的值替代 --- const限定的變量,在編譯階段,編譯器會進行常量折疊的一些優化操作。所謂常量折疊,就是化簡常量表達式,即當遇到const限定的變量時,會用相應的值進行替換,使其變為一個立即數。當然,不是所有情況都能進行常量折疊,如對常量進行取地址操作。
eg:int main(){ const int a = 2; int *ptr = const_cast<int *>(&a);//改變常量屬性 *ptr = 5; printf("a = %d\n" ,a);//此處進行常量折疊 printf("&a = %x\n", &a); printf("*ptr = %d\n",*ptr); printf("ptr = %x\n",ptr); printf("a = %d\n" ,a);////此處進行常量折疊 printf("&a = %x\n", &a); return 0; }
運行結果:
分析:
按&a,ptr,&a順序輸出的地址都不變,即說明處理的一直都是常量區的那塊內存。當強行改變常量屬性,對內存進行寫操作時,再次輸出常量 a 的值,還是原來的 2 ,而不是 3 ,此處說明,這里已經是被編譯器優化了,編譯期進行了值替代。與volatile一起使用時,編譯器不會做常量折疊等優化 volatile不允許編譯器對數據做出假定,必須要重重讀數據,而不是進行優化 eg: int main(){ volatile const int a = 1; printf("change before: %d", a); int *ptr = const_cast<int*>(&a); *ptr = 4; printf("change after: %d", a); return 0; }
運行結果
ii. 運行時常量 -- 如果能確定一個變量,在其生存期之間,一直保持值不變,也可以用const來限定。
eg:int main(){ const int a = cin.get();//get()取單個字符 cout <<a; return 0; }
運行結果:
分析:
說明可以是運行時的const變量
iii. const可以用來限定集合,如數組。編譯器在編譯階段,不能把集合放在它的符號表中,而是必須分配內存,表明“不能改變的一塊存儲”。在編譯時期,集合中的值,是不可使用的。
(所謂分配內存,是指靜態分配空間,是指在編譯時期,就可以計算出其大小的,將這些信息寫到可執行文件一些區,在執行時,不必執行額外的代碼來為其分配內存,而是程序直接將這些數據,與物理地址建立映射關系。如,靜態變量與全局變量)
eg:int main(){ const int array[3]={0,1,2}; float temp[array[0]];//此種行為編譯器會報錯 //因為array數組中的值在編譯時不能使用,是運行時的存儲值 }
Tips:
C++中const與C中的const的區別:
const默認內部連接,作用域僅在本文件,而C中,默認外部連接。 -
constexpr
constexpr是C++11提出的,目的是讓編譯器在編譯時期判斷表達式是否是常量表達式,若是,則可以在編譯時就進行一些優化工作,提高效率。如常量折疊等;若不是,即報錯。
i. constexpr修飾變量constexpr int a = 1;//相當於const限定的編譯時可確定值的用法 constexpr int b = cin.get();//編譯器報錯,此為運行時確定的存儲
ii. constexpr修飾函數
當constexpr修飾的函數返回值用作常量表達式時,如用作數組聲明中的維度,要求該函數中,定義的所有變量均為編譯時可決定的值,且不能進行取地址等復雜操作,並且要求傳入的參數也必須是常量表達式。
若返回結果不用做常量表達式,即權當普通函數使用。
eg:返回值作為常量表達式使用:程序可編譯執行 constexpr int test(int a) { int b = 2;//個人猜測當返回值用作常量表達式時,b會當做constexpr變量使用 //printf("b = %d, &b = %x\n", b, &b);//若注釋去掉,編譯器報錯,constexpr函數無法生成常數表達式 return b; } int main() { int a = 1; int b = 1; //int array[test(a+b)]; //若注釋去掉,編譯器報錯,變量a的值不可用作常量 int array[test(1)]; return 0; }
返回值不做常量表達式使用: 傳參不做要求(在以上程序加test(a+b);進行測試,通過) 返回值是否為常數表達式不做要求
- 一個constexpr修飾的指針的初始值必須是nullptr或者0,或者是存儲於某個固定地址中的對象(如函數體外定義的變量或函數體內的static變量),且constexpr修飾的是指針,而不是指針指向的對象。(即xxx *const ptr;)