字面常量_(C语言)

**字面常數**（literal constant），是 C 程式設計語言與 C++ 語言的詞法上的概念（lexical conventions），是指源程式中表示固定值的符號（token）。

下述內容遵從 C11 與 C++11 語言標準。

## 整型字面常數

包括下述的數值進位：

* 10 進位： 如 1234;
* 8 進位：如 0373;
* 16 進位：如 0x2a7;
* 2 進位（從 C++14 開始）：如 0b101; 或者 0B0101;

整型字面量轉化為整數類型表示，依照下述順序：

* 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。

沒有字首字元的稱為**普通字元字面量**(ordinary character literal) 或窄字元字面常數（narrow-character literal）

只包含執行字元集（execution character set）中一個可表示字元的普通字元字面量，如：'a'，則其類型為 char，其值等於該字元在執行字元集中的編碼值。實際上，編譯器在 token 分析階段，通常就會把字元與字串在原始檔中的編碼串轉換為指定或者執行字元集的編碼串。

包含多個字元的普通字元字面常數，被稱為**多字元字面量**（multicharacter literal）。多字元字面量，以及包含執行字元集（execution character set）中一個不可表示字元的普通字元字面量，被有條件支援，具有類型 int，其值為實現定義。各常見 C/C++ 編譯器一般都支援如下例子：

需要注意的是，在 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。

```

字串字面量的值預設為工作字元集的編碼。使用編碼字首（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）。由使用者給出字面常數的字尾，並給出字面量運算子函式（或模板）的定義。編譯器可在執行期或編譯期把帶有這樣字尾的整型、浮點型、字元型、字串型的字面量通過呼叫使用者的字面量運算子函式（或模板），生成指定資料類型的物件。例如，

## 參考文獻