字符字面量
字符常量是單引號引起來的字符序列 ,字符常量通常由一個字符組成 , 也可以包含多個字符,比如'\n',在C中字符字面量是以int類型表示的,因此字符字面量的sizeof運算結(jié)果是4曲伊。更確切地說卓起,在C中字符字面量是ASCII系統(tǒng)編碼對應的索引,所以例如'a'就對應整數(shù)37.'A'對應的就是65.可見"C Programming Language"這本書的第35-36頁的相關(guān)文檔.
必須要提到一個概念就是數(shù)據(jù)對象(Data Objects),這里簡稱對象,這是很多寫C教程很少提到的一個概念,也不是我憑空捏造的.文獻來源http://www.newtonlabs.com/ic/ic_5.html
像基于C衍生出來其他高層語言所理解的"對象"是有些區(qū)別的.C中的對象更偏向于內(nèi)存模型,數(shù)據(jù)對象就是規(guī)范C中所有基本數(shù)據(jù)類型的兩個基本屬性.
- 數(shù)據(jù)值(value):就是存儲在某片特定數(shù)據(jù)類型大小的內(nèi)存塊中二進制數(shù)據(jù)的表示形式。
- 存儲位置 (storage location),該存儲位置由操作系統(tǒng)為分配一個該位置對應的地址
備注:一般來說,數(shù)據(jù)值對應的是右值,而存儲位置對應的是左值,也就是變量悯周;
而字符字面量是數(shù)據(jù)對象中的一個數(shù)據(jù)值,C的編譯器根本不允許你這么做,因為數(shù)據(jù)值只是在某個一字節(jié)的內(nèi)存塊中二進制碼表示形式,所以不會存在用取址操作符(&)去獲取一個值的地址這種荒謬的說法.
char類型和字符字面量
其實這個問題就是上面同一個問題,換了一種問題描述而已挪蹭。
因為在C中的聲明一個char類型的變量就是一個左值(存儲位置),它僅僅在char類型的變量對應的內(nèi)存空間中保存字符字面量對應的ASCII編碼的int類型的值而已.例如:
char c='A';
就告訴了C編譯器完成兩件事:
- 需要分配char類型該有的內(nèi)存空間(通常是8bit,但實際大小跟運行的硬件平臺有關(guān));
-
在為char變量分配的內(nèi)存空間中保存ASCII碼字面量'A'對應的整數(shù)值65(二進制形式)
字符字面量在內(nèi)存中的表示
字符字面量:C vs C++
我們來考慮一下兩個簡單的代碼
- 在C中char是一個基本的數(shù)據(jù)對象,默認1個字節(jié)式廷;
- 在C++中同樣繼承了C的char類型,當然默認也是1個字節(jié);
printf("%ld",sizeof(char)); //1個字節(jié)
C++中的字符字面量
下面這個代碼的輸出就非常有意思了
printf("%ld",sizeof('a'));
- 在C中,因為'a'本質(zhì)上就是返回ASCII編碼對應的int類型的整數(shù)值,而該C中int默認就是4個字節(jié).這個沒疑問.
- 在C ++語言中,當字符字面量被sizeof作為一個操作數(shù)時,C++編譯器就會在調(diào)用代碼的上下文隱含地定義了 一個臨時char變量,并且該字符字面量對應的編碼值保存到該臨時變量中.因此你在C++源文件中用sizeof('a')會被替換為 以下偽代碼
char tmp='a'; sizeof(tmp); //打印char類型變量的尺寸,而不是ASCII碼對應int類型的尺寸
來源中文翻譯:窄字符字面量或原始字符文字华弓,例如 'a'或'\ n'或'\ 13'食零。 這樣的字面量具有char類型,并且值等于執(zhí)行字符集中c-char表示的值寂屏。 如果c-char不能表示為執(zhí)行字符集中的單個字節(jié)贰谣,則字面量具有int類型和實現(xiàn)定義的值。
文獻來源:https://en.cppreference.com/w/cpp/language/character_literal
其中為什么C++中要字符字面量視為一個char類型來看待其中一個例證那就是C++為了兼顧函數(shù)在面向?qū)ο缶幊伤枷胫械膬蓚€特性,就是同樣函數(shù)名稱的原型的多態(tài)和重寫的這兩個特性,試想以下例子迁霎。
char kiss_me(char c);
int kiss_me(int c);
void main(void){
kiss_me('A');
kiss_me(65);
}
作一個反證的假設,如果C++將字符串字面量和C的約定是一樣的話,那么字符字面量就視為一個ASCII碼對應的int類型的整數(shù)值,那么C++就無法分辨下面的關(guān)于kiss_me的不同函數(shù)原型了,這就很矛盾了,反證C++中要字符字面量視為一個char類型的必要性.
區(qū)分字符字面量和字符串字面量
字符串字面量即用雙引號括著的多個字符字面量就是字符字面量的序列吱抚。并且字符串字面量是存在于字面量池中,字面量池中的字符串字面量是在程序初始化的時候填出入字面量池中考廉,其生命周期直到加載該字面串字面量的程序退出秘豹。我們可以將字面量池想象成一份“簽到表”,而字符串字面量就比喻成眾多的簽名,但這份簽到表比較特殊昌粤。
字面量池的特征
字面量池就是位于C/C++中內(nèi)存布局中較向低地址的一片內(nèi)存區(qū)域既绕,這片內(nèi)存區(qū)域的通常是公開的啄刹,整個程序的上下文用到所有字符字面量都會在初始化過程中填入該池,并且全局維護整個程序上下文的唯一副本岸更。也就是其他內(nèi)存區(qū)域用到字符串字面量副本是從該池中“初次”(注意我的用詞)拷貝過去的發(fā)源地鸵膏。該池的特征如下。
- 不允許有重復的字符串字面量出現(xiàn)怎炊,這樣做的目的是為了節(jié)省空間,例如"hello word"不可能出現(xiàn)兩次,但“hello world ”,注意后者的字符字面量最后是一個空格即
因此被字面量池認為不同于前者的字符串字面量谭企。下面的例子同樣的字符串字面量出現(xiàn)了兩次,但兩個char指針都指向字面量池中同一個字符串字面量,也就是說指向其池中的內(nèi)存地址是一樣的评肆。char s[]=['h','e','l','l','o',' ','\0'];
int main (void){ char *s="it-dog"; char *b="it-dog"; //指針s和b指向同一個字符字面量的內(nèi)存地址 //0x557375b2b21d }
- “字符串字面量”之間都是連貫的,每個字符串字面量之間結(jié)尾存在一個特殊字符'\0'
- 每個字符串字面量都存在一個唯一的內(nèi)存地址,也就是說前一個字符串字面量末端字符'\0'的下一個字符字面量的內(nèi)存地址就是對應字符串字面量的內(nèi)存地址债查,即&“hello word”這樣的用法在C/C++中是合法的。
char* s="hello word"; &"hello word" //和上面的 printf("%lp\n",&s) //和下一條語句輸出的內(nèi)存地址是一樣的 printf("%lp\n",&"hello word")
- 字面量池中的字符串字面量是只讀的,但池外中的其他內(nèi)存區(qū)域的變量持有的相同的字符串字面量只是一個相同的副本而已瓜挽,只要沒有聲名為常量字符串,都可以修改盹廷。