C 語言 99, 11 新政策

1. C99

已經(jīng)是上個世紀的最后一版 C 語言,但由于譚浩強當年沒有跟上,導致我們很多人也沒跟上。

1.1. 語法級

1.1.1. 基本概念

1.1.1.1. 注釋

C99 起支持 C++ 式的雙斜杠單行注釋。

1.1.1.2. 標識符

標識符中可使用由 \u\U 轉(zhuǎn)義的 Unicode 字符。\u 后面為 4 位 16 進制數(shù),\U 后面為 8 位 16 進制數(shù),該 16 進制數(shù)表示一個 Unicode 碼點,如:

int \u6211 = 42;

1.1.2. 預處理

1.1.2.1. 宏

1.1.2.1.1. 宏定義

#define 指令的形參列表尾部可使用變長參數(shù)列表 ...,使用 __VA_ARGS__ 訪問變長參數(shù),如:

#define print1(s, ...) printf(s, 1, __VA_ARGS__)

print1("%d %d %d\n", 2, 3); // printf("%d %d %d\n", 1, 2, 3);

使用 #__VA_ARGS__ 將變長參數(shù)放入一對雙引號中,并用一個逗號與空格隔開(無論代碼如何排版),如:

#define print2(a, b, ...) printf(#__VA_ARGS__, a, b)

print2(1, 2, %d,
    %d); // printf("%d, %d", 1, 2);

1.1.2.1.2. 預定義宏

__STDC_VERSION__ 的值為 199901L。

新增宏 __STDC_HOSTED__,當目標環(huán)境為操作系統(tǒng)之下則為 1,目標環(huán)境無操作系統(tǒng)則為 0

1.1.3. 表達式

1.1.3.1. 字面量

新增整型字面量后綴 llLL,表示 long long 類型。新增整型字面量后綴 ullUlluLLULL,表示 unsigned long long 類型。

新增字符字面量轉(zhuǎn)義符 \u\U\u 后面為 4 位 16 進制數(shù),\U 后面為 8 位 16 進制數(shù),該 16 進制數(shù)表示一個 Unicode 碼點,如 '\u6211'。

新增復合字面量用以表達數(shù)組、結(jié)構(gòu)體、聯(lián)合體,其形式為 (類型){初始化列表},如:

int i = (int[]){ 1, 2 }[1];

1.1.3.2. 初始化

初始化列表中的項,新增一種可用形式 指派器列表=初始化器。其中指派器列表的形式為數(shù)組的下標表達式 [常量表達式] 或結(jié)構(gòu)體、聯(lián)合體的成員訪問表達式 .標識符。

對于數(shù)組的初始化,可使用指派器列表指定下標,后續(xù)的下標會在此指定值的基礎(chǔ)上依次加 1,如:

int ar[5] = { 1, [4] = 5, [1] = 2, 3 }; // { 1, 2, 3, 0, 5 }

按照初始化列表的順序,后面相同下標的賦值會覆蓋前面,如:

int ar[5] = { [3] = 5, 5, [1] = 2, 3, 4 }; // { 0, 2, 3, 4, 5 }

可用于嵌套數(shù)組,此時指派器列表可以是嵌套的數(shù)組下標表達式,用于指定當前嵌套深度下的數(shù)組下標,如:

int ar[3][3] = { [0] = { 1, [1] = 2, 3 }, { [1] = 5, 6 }, [2][1] = 8, 9 }; // { { 1, 2, 3 }, { 0, 5, 6 }, { 0, 8, 9 } }

對于結(jié)構(gòu)體的初始化,可使用指派器列表指定成員,后續(xù)的成員會依照結(jié)構(gòu)體中成員聲明的順序,后面相同成員的賦值會覆蓋前面,如:

typedef struct S {
    int a, b, c, d, e;
} S;

S s = { 1, 1, .e = 5, .b = 2, 3 }; // { 1, 2, 3, 0, 5 }

可用于嵌套結(jié)構(gòu)體,此時指派器列表可以是嵌套的成員訪問表達式,用于指定當前嵌套深度下的成員,如:

typedef struct S {
    int a, b, c;
} S;

typedef struct R {
    S x, y, z;
} R;

R r = { .x = { 1, .b = 2, 3 }, { .b = 5, 6 }, .z.b = 8, 9 }; // { { 1, 2, 3 }, { 0, 5, 6 }, { 0, 8, 9 } }

1.1.4. 聲明

聲明不再限定于只能出現(xiàn)在塊的開頭。這是 C99 中譚浩強錯過的最重要的一點,以致于到了二十一世紀我們許多人還遵守著這個限制。

1.1.4.1. 限定符

在函數(shù)聲明中,限定符可用于數(shù)組形參的方括號內(nèi),數(shù)組形參會轉(zhuǎn)化為指針形參,限定符轉(zhuǎn)而修飾該指針,如:

int f(int[const]); // int f(int* const)
int g(const int[]); // int g(const int*)
1.1.4.1.1. restrict

新增限定符關(guān)鍵字 restrict 以修飾一個指針類型的左值,提示編譯器在該指針的作用域內(nèi),如果經(jīng)由該指針所訪問的內(nèi)存會被修改,則該內(nèi)存僅經(jīng)由該指針訪問。根據(jù)此限定符,編譯器能作出更大膽的優(yōu)化。

1.1.5. 語句

1.1.5.1. for

for 語句的初始化子句可以是一個聲明,該聲明的作用域為整個循環(huán)體,包括循環(huán)體的條件表達式和迭代表達式,如:

for (int i = 0; i < 42; i++) {

1.1.6. 類型

1.1.6.1. 標量

新增至少 64 位的有符號整型 long long 和無符號整型 unsigned long long。

新增算術(shù)類型關(guān)鍵字 _Bool,該類型的值只能是 01。通常使用頭文件 stdbool.h 中定義的宏 bool、truefalse。所有非零值的標量轉(zhuǎn)化為 _Bool 類型后其值為 1,零值的標量轉(zhuǎn)化為 _Bool 類型后其值為 0,注意這與轉(zhuǎn)化為其他某種整型時的區(qū)別,特別是將其他某種整型用作布爾類型時,如:

#include <stdbool.h>

bool b1 = false; // _Bool b1 = 0;
bool b2 = NULL; // 0
bool b3 = 0.5; // 1
#define Bool unsigned char
Bool b4 = 0.5; // 0

1.1.6.2. 數(shù)組

新增變長數(shù)組。聲明一個數(shù)組時如果長度不是一個常量,則聲明為變長數(shù)組,如:

void f(int i) {
    int ar[i];

變長數(shù)組的聲明不能使用初始化列表。變長數(shù)組的長度在運行時確定,內(nèi)存在運行時分配,長度在其生命周期內(nèi)不可變。變長數(shù)組的生命周期只能在函數(shù)塊內(nèi)部,不能作為全局變量、結(jié)構(gòu)體和聯(lián)合體的成員。

變長數(shù)組作為函數(shù)形參時,在函數(shù)原型聲明中,使用 * 作為數(shù)組長度,該數(shù)組同樣會轉(zhuǎn)化為元素類型的指針,如:

void f(int[*]); // void f(int*)

變長組數(shù) T[n]sizeof 操作仍是計算整個數(shù)組的長度,即 sizeof(T) * n,但是在運行時計算,因此不再是常量。

1.1.6.3. 枚舉

允許枚舉聲明中的最后一項的后面出現(xiàn)逗號。

1.1.6.4. 結(jié)構(gòu)體

新增結(jié)構(gòu)體柔性數(shù)組成員。柔性數(shù)組成員只能作為不完整數(shù)組類型而聲明在最后,如:

typedef struct S {
    char c;
    int ar[];
} S;

S *s = (S*)malloc(sizeof(S) + sizeof(int) * 3);
s->ar[2] = 42;

結(jié)構(gòu)體的初始化列表、sizeof 操作符、賦值操作符會忽略柔性數(shù)組成員。包含柔性數(shù)組成員的結(jié)構(gòu)體不能作為數(shù)組元素和結(jié)構(gòu)體成員。

結(jié)構(gòu)體位域字段可使用 _Bool 類型,寬度只能為 1,如:

struct S {
    _Bool b: 1;
};

1.1.7. 函數(shù)

新增函數(shù)域內(nèi)的預定義靜態(tài)局部變量 static const char __func__[],內(nèi)容為當前函數(shù)名。

1.2. 標準庫級

1.2.1. 類型

頭文件 <stdbool.h>

定義了宏 bool、true、false。truefalse 是整型常量 10。

頭文件 <stdint.h>

定義了確定長度的整型 typedef int8_t、int16_t、int32_t、int64_t、intptr_t、uint8_t、uint16_tuint32_t、uint64_tuintptr_t 等。

1.2.2. 數(shù)值計算

新增大量數(shù)值計算函數(shù)。

參考文檔

2. C11

和 C++ 同年推出的新世紀第一個 C 語言新標準,譚浩強更加跟不上了。

2.1. 語法級

2.1.1. 基本概念

2.1.1.1. 對齊

新增操作符關(guān)鍵字 _Alignof 查詢類型的對齊,返回類型為 size_t,返回值為常量,如:

typedef struct S {
    int i;
    char c;
} S;

_Alignof(S) // 4;

新增關(guān)鍵字 _Alignas 以聲明對齊,其形式為 _Alignas(整型常量表達式或類型),對齊為整型常量表達式的值或類型的 _Alignof 的值,如:

typedef struct S {
    _Alignas(16) char s[42];
};

2.1.2. 預處理

2.1.2.1. 宏

__STDC_VERSION__ 的值為 201112L。

新增宏 __STDC_UTF_16__,當 char16_t 使用 UTF-16 編碼則為 1。

新增宏 __STDC_UTF_32__,當 char32_t 使用 UTF-32 編碼則為 1。

新增宏 __STDC_ANALYZABLE__,當編譯器支持可分析性則為 1

新增宏 __STDC_LIB_EXT1__,當標準庫包含帶邊界檢查的特定 API 則為 201112L。

新增宏 __STDC_NO_ATOMICS__,當編譯器不支持原子類型且標準庫不包含原子類型 API 則為 1。

新增宏 __STDC_NO_COMPLEX__,當編譯器不支持復數(shù)類型且標準庫不包含復數(shù)類型 API 則為 1。

新增宏 __STDC_NO_VLA__,當編譯器不支持變長數(shù)組則為 1。

2.1.3. 表達式

2.1.3.1. 字面量

新增字符字面量前綴 u,字符類型為 char16_t,通常是 UTF-16 編碼。如 u'我'(char16_t)0x6211,若字符的 Unicode 碼點對應的 UTF-16 編碼超出單個編碼單元,則依照編譯器的具體實現(xiàn)。

新增字符字面量前綴 U,字符類型為 char32_t,通常是 UTF-32 編碼。如 U'我'(char32_t)0x00006211,若字符的 Unicode 碼點對應的 UTF-32 編碼超出單個編碼單元(這都能超過?),則依照編譯器的具體實現(xiàn)。

新增字符串字面量前綴 u8,字符類型為 char,UTF-8 編碼。如 u"是我"(char[]){ 0xe6, 0x98, 0xaf, 0xe6, 0x88, 0x91, 0 }。

新增字符串字面量前綴 u,字符類型為 char16_t,通常是 UTF-16 編碼。如 u"是我"(char16_t[]){ 0x662f, 0x6211, 0 }。

新增字符串字面量前綴 U,字符類型為 char32_t,通常是 UTF-32 編碼。如 U"是我"(char32_t[]){ 0x0000662f, 0x00006211, 0 }

2.1.3.2. 泛型選擇表達式

新增關(guān)鍵字 _Generic 以提供泛型選擇表達式,其形式為 _Generic(控制表達式, 關(guān)聯(lián)列表)。關(guān)聯(lián)列表中的關(guān)聯(lián)項由逗號分隔,關(guān)聯(lián)項的形式為 類型名: 表達式default: 表達式,各表達式不能為逗號表達式??刂票磉_式會經(jīng)歷左值轉(zhuǎn)換(去掉頂層的類型限定符、數(shù)組到指針、函數(shù)到指針)但不會求值,轉(zhuǎn)換后的類型與各關(guān)聯(lián)項中的類型名進行匹配,返回匹配到的關(guān)聯(lián)項中的表達式,若無 default 項且無匹配項則編譯出錯。如:

int i = 42;
j = _Generic(i,
    char: i + 1,
    int: i + 2,
    default: i + 3
); // 44

2.1.3.3. 靜態(tài)斷言

新增關(guān)鍵字 _Static_assert 以提供靜態(tài)斷言,其形式為 _Static_assert(整型常量表達式, 字符串字面量),靜態(tài)斷言在編譯時進行,當整型常量表達式的值為 0 則發(fā)生編譯錯誤,字符串字面量會出現(xiàn)在錯誤信息中。

2.1.4. 聲明

2.1.4.1. 存儲期說明符

新增關(guān)鍵字 _Thread_local 以聲明變量的存儲期為新增的線程存儲期。線程存儲期為該線程的整個執(zhí)行過程,變量在線程啟動時初始化。不能用于函數(shù)聲明。用于在塊作用域中聲明時,必須與 staticextern 存儲期組合。

2.1.4.2. 原子類型

新增關(guān)鍵字 _Atomic 以聲明變量為原子類型。

不能用于數(shù)組和函數(shù)聲明,如:

typedef int pair[2];
// _Atomic pair p;

但可聲明元素為原子類型的數(shù)組,如:

_Atomic int p[2];

2.1.5. 類型

2.1.5.1. 標量

新增 16 位和 32 位無符號字符類型 char16_tchar32_t,通常是 typedef。

2.2. 標準庫級

2.2.1. 線程

新增線程支持庫

頭文件 <threads.h>

新增線程、線程局部存儲和同步原語操作函數(shù)。

頭文件 <stdatomic.h>

新增原子操作函數(shù)。

參考文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容