字面常量 (C語言)
字面常量(literal constant),是C程序設計語言與C++語言的詞法上的概念(lexical conventions),[1]是指源程序中表示固定值的符號(token)。[2]
整型字面常量
編輯包括下述的數值進制:
- 10進制: 如1234;
- 8進制:如0373;
- 16進制:如0x2a7;
- 2進制(從C++14開始):如0b101; 或者0B0101;
整型字面量轉化為整數類型表示,依照下述順序:[3]
- int
- unsigned int
- long int
- unsigned long int
- long long int
- unsigned long long int
其中,10進制表示的字面量僅考慮有符號的整數類型;而8進制或16進制的字面量先考慮能否用有符號的整數類型表示,如不能再考慮能否用同樣長度的無符號整數類型表示。例如,字面量0x87654321,這是一個正值,用4字節長的int無法表示,編譯器就會自動選用unsigned int來表示該字面量。
整型字面量可以使用後綴u U ul UL ull ULL明確表示各種無符號整型;使用後綴l L ll LL表示該字面量至少為long或為long long型。
C++14引入了千分位分隔符。例如:
auto integer_literal = 1'000'000;
C++14還引入了二進制字面量,例如:
auto perm = 0b100110111;
浮點型字面常量
編輯包括下述的數值進制:
- 10進制:如 2.3 2e-3 2.e-5 2.12e15等。如果不是科學計數法形式,就必須有小數點,小數點前或後的數字可省略;
- 16進制(C語言特有,C99引入):如 0x1p10(值為102410) 0x1.0004p10(值為1024.062510)等。
沒有後綴的浮點型字面量具有double類型。使用後綴 f F l L表示float或long double類型。
C++14引入了千分位分隔符。例如:
auto dd = 1'234.567'8;
字符型字面常量
編輯字符型字面量(character literal)是用單引號括起來的一個或多個字符,可選前綴字符有u、U、L。[4]
沒有前綴字符的稱為普通字符字面量(ordinary character literal)或窄字符字面常量(narrow-character literal)
只包含執行字符集(execution character set)中一個可表示字符的普通字符字面量,如:'a',則其類型為char,其值等於該字符在執行字符集中的編碼值。實際上,編譯器在token分析階段,通常就會把字符與字符串在源文件中的編碼串轉換為指定或者執行字符集的編碼串。
包含多個字符的普通字符字面常量,被稱為多字符字面量(multicharacter literal)[4]。多字符字面量,以及包含執行字符集(execution character set)中一個不可表示字符的普通字符字面量,被有條件支持,具有類型int,其值為實現定義。[5]各常見C/C++編譯器一般都支持如下例子:
// Multicharacter literals
int m0 = 'abcd'; // int, value 0x61626364
需要注意的是,在C語言中字符常量'a'具有int類型,sizeof('a')等於sizeof(int);在C++語言中字符字面量'a'具有char類型,sizeof('a')為1。
使用前綴L u U分別表示wchar_t char16_t char32_t等字符類型的字面量。例如u'y'
。一個wchar_t類型的字面量的值,就是該字符在執行寬字符集(execution wide-character set)中的編碼值;單如果該字符在執行寬字符集中不可表示,則其值是實現定義的。一個char16_t類型的字面量的值,就是該字符在Unicode編碼中的16比特的碼位;顯然該字符必須屬於Unicode中的基本多文種平面才能用16比特來編碼,否則程序是病態的。一個char32_t類型的字面量的值,就是該字符在Unicode編碼中的32比特的碼位。這些字符類型在x86平台均為小端序表示。char16_t字面量或char32_t字面量,如果包含多個字符,是病態的。wchar_t 字面量,如果包含多個字符,其值是實現定義的。
由於字符型字面量可能不屬於C/C++的token的字符範圍,這就需要用反斜線\
開始的轉義序列來表示一個字符值:
- 簡單轉義序列:\' \" \? \\ \a \b \f \n \r \t \v 共計11個字符;
- 八進制轉義序列:如 \1 \12 \123等等,直至不是八進制數字為止,最多使用3位八進制數字;
- 十六進制轉義序列:如 \x1abf4 ,可以使用任意多的十六進制數字,直至不是十六進制數字為止;
- 通用字符名(universe-character name):
\u
後面必須跟4個十六進制數字(不足四位前面用零補齊),表示Unicode中在0至0xFFFF之內的碼位(但不能表示0xD800到0xDFFF之內的碼點,Unicode標準規定這個範圍內的碼位保留,不表示字符); - 32位的通用字符名:
\U
後面必須跟8個十六進制數字(不足八位前面用零補齊),表示Unicode中所有可能的碼位(除0xD800到0xDFFF之外)。
上述兩種Unicode轉義序列的字符表示法,由編譯器自動轉換為相應的字符內部編碼格式。如為wchar_t字符。
如果字符字面量的值超過了char、char16_t、char32_t、wchar_t實現定義的範圍,那麼其值是實現定義的。例如,源文件是Latin-1編碼,執行字符集為utf-8,則char c='ö';
中的字符值將被編譯器從Latin-1編碼的單字節的0xD6自動轉為utf-8編碼的雙字節的0xC3B6,在目標文件與可執行文件中字符c的值是'ö'的utf-8編碼值的最後一個字節值即0xB6。
為支持老的代碼,C語言規定了三字符組替換,在掃描處理C語言源文件時,替換下述的3字符出現為1個字符:
三字符組 | 替換為 |
---|---|
??= |
#
|
??/ |
\
|
??' |
^
|
??( |
[
|
??) |
]
|
??! |
|
|
??< |
{
|
??> |
}
|
??- |
~
|
如果希望在源程序中有兩個連續的問號,且不希望被預處理器替換,這種情況出現在字符字面常量、字符串字面常量或者是程序注釋中,可選辦法是用字符串的自動連接:"...?""?..."
或者轉義序列:"...?\?..."
。
從Microsoft Visual C++ 2010版開始,該編譯器默認不再自動替換三字符組。如果需要使用三字符組替換(如為了兼容古老的軟件代碼),需要設置編譯器命令行選項/Zc:trigraphs
gcc默認不識別三字符組,並會給出編譯警告。在指定了C/C++標準時,gcc才會識別三字符組,但仍會給出編譯警告。
字符串字面常量
編輯C語言經典的ASCII-0字符串,例如"Hello world!"。
由R"<someChars>opt(
與 )<someChars>opt"
括起來的字符串字面量叫做raw string literal,這可以避免大量使用反斜線轉義字符造成的令人眼花繚亂的傾斜牙籤綜合徵,特別適用於定義正則表達式的模式字符串時。例如:
std::string filePath = R"(C:\Foo\Bar.txt)";
std::regex re{ R"abc(s/"\([^"]*\)"/'\1'/g)abc" }; //如果字符串包含了)"这两个字符的组合,可选别的分界符,如abc
。但这个分界符序列的长度最多16。
字符串字面量的值默認為工作字符集的編碼。[6]使用編碼前綴(encoding-prefix): u8 u U L 指出字符串字面量的值為UTF-8、char16_t、char32_t、wchar_t的編碼序列。例如,
char ss[]=u8"Hi世界";// ss数组内保存的是'H'、'i'、'世'、'界'这四个字符的UTF-8的编码值
相鄰的兩個字符串將被編譯器自動連接為一個字符串。如:
char ss[]="Hello" " world.";
枚舉常量
編輯枚舉類型實質上是取有限個值的整型。根據C與C++98標準,枚舉常量屬於定義了枚舉類型的那個作用域,而不屬於這個枚舉類型的內部作用域。C++發明人Bjarne Stroustrup稱之為枚舉常量的作用域不受限(unscoped)現象,這會造成命名衝突,與枚舉常量是「有類型的常量」的初衷不符,與面向對象程序設計的原則嚴重相悖。例如:
enum FileAccess { Read = 0x1, Write = 0x2, }; FileAccess access = ::Read; // 正确 FileAccess access = FileAccess::Read; // 错误 enum FileShare { Read = 0x1, // 重定义错误 Write = 0x2, // 重定义错误 };
C++11為解決上述問題,引入了「枚舉類」(enum class)。在定義枚舉類型時,enum關鍵字後面加上class關鍵字(或struct關鍵字);引用枚舉常量時,必須加上枚舉類型名使其為作用域限定 (scoped)。這避免了枚舉常量的命名衝突;同時也避免了不同枚舉類型的值的隱式類型轉換,從而保證了枚舉類型是強類型(strongly typed)的。例如:
enum class Color { RED, BLACK }; Color c = Color::RED;
C++11還允許指定枚舉類型的存儲類型,這使得枚舉類型的前向聲明(forward declaration)成為可能。例如:
enum class Color : char ; // forward declaration void foo (Color *p);// ... // ... enum class Color : char { RED, BLUE }; // definition
布爾型字面常量
編輯有兩個bool類型的字面常量:true false
指針型字面常量
編輯C++11定義了一個字面常量nullptr,其類型是std::nullptr_t。但std::nullptr_t既不是指針類型也不是到成員的指針類型。
用戶定義的字面量
編輯C++11新增加了用戶定義的字面常量(user defined literal)。由用戶給出字面常量的後綴,並給出字面量運算符函數(或模板)的定義。編譯器可在運行期或編譯期把帶有這樣後綴的整型、浮點型、字符型、字符串型的字面量通過調用用戶的字面量運算符函數(或模板),生成指定數據類型的對象。例如,
#include<iostream>
struct S{
int value;
};
S operator ""_mysuffix(unsigned long long v) //用户定义字面量运算符的实现
{
S s_;
s_.value=(int)v;
return s_;
}
int main()
{
S sv;
sv=101_mysuffix; //这里的101是类型S的字面量
std::cout<<sv.value<<std::endl;
return 0;
}
參考文獻
編輯- ^ C++2011 ISO/IEC 14882 §2.14 Literals
- ^ C++11標準§2.14.1的腳註21:The term 「literal」 generally designates, in this International Standard, those tokens that are called 「constants」 in ISO C. 即C語言的constants與C++的literal是一碼事,屬於詞法分析時的token。
- ^ C99標準,第6.4.4.1節,條款5
- ^ 4.0 4.1 ISO 14882 -- 《C++14 specification》 section 2.14.3, entry 1.
- ^ C99語言規範 6.4.4.4p10有類似內容: "The value of an integer character constant containing more than one character (e.g., 'ab'), or containing a character or escape sequence that does not map to a single-byte execution character, is implementation-defined."
- ^ 具體是哪一種工作字符集編碼,由編譯器採用缺省設置,如Visual C++默認使用當前操作系統的缺省代碼頁,簡體中文Windows就是gbk編碼,Linux上的gcc一般默認為utf8編碼;也可以作為命令行參數指定給編譯器,如gcc的"-fexec-charset= ".