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. 字面量
新增整型字面量后綴 ll
或 LL
谒府,表示 long long
類型拼坎。新增整型字面量后綴 ull
或 Ull
或 uLL
或 ULL
,表示 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ù)的下標會在此指定值的基礎上依次加 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
新增限定符關鍵字 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ù)類型關鍵字 _Bool
赊堪,該類型的值只能是 0
或 1
面殖。通常使用頭文件 stdbool.h
中定義的宏 bool
、true
雹食、false
畜普。所有非零值的標量轉(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
涡扼。true
和 false
是整型常量 1
和 0
。
頭文件
<stdint.h>
定義了確定長度的整型 typedef int8_t
盟庞、int16_t
吃沪、int32_t
、int64_t
什猖、intptr_t
票彪、uint8_t
、uint16_t
不狮、uint32_t
降铸、uint64_t
、uintptr_t
等摇零。
1.2.2. 數(shù)值計算
新增大量數(shù)值計算函數(shù)推掸。
2. C11
和 C++ 同年推出的新世紀第一個 C 語言新標準,譚浩強更加跟不上了驻仅。
2.1. 語法級
2.1.1. 基本概念
2.1.1.1. 對齊
新增操作符關鍵字 _Alignof
查詢類型的對齊谅畅,返回類型為 size_t
,返回值為常量噪服,如:
typedef struct S {
int i;
char c;
} S;
_Alignof(S) // 4;
新增關鍵字 _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. 泛型選擇表達式
新增關鍵字 _Generic
以提供泛型選擇表達式鼻听,其形式為 _Generic(控制表達式, 關聯(lián)列表)
。關聯(lián)列表中的關聯(lián)項由逗號分隔联四,關聯(lián)項的形式為 類型名: 表達式
或 default: 表達式
撑碴,各表達式不能為逗號表達式〕眨控制表達式會經(jīng)歷左值轉(zhuǎn)換(去掉頂層的類型限定符醉拓、數(shù)組到指針、函數(shù)到指針)但不會求值收苏,轉(zhuǎn)換后的類型與各關聯(liá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)斷言
新增關鍵字 _Static_assert
以提供靜態(tài)斷言排吴,其形式為 _Static_assert(整型常量表達式, 字符串字面量)
,靜態(tài)斷言在編譯時進行懦鼠,當整型常量表達式的值為 0 則發(fā)生編譯錯誤傍念,字符串字面量會出現(xiàn)在錯誤信息中。
2.1.4. 聲明
2.1.4.1. 存儲期說明符
新增關鍵字 _Thread_local
以聲明變量的存儲期為新增的線程存儲期葛闷。線程存儲期為該線程的整個執(zhí)行過程憋槐,變量在線程啟動時初始化。不能用于函數(shù)聲明淑趾。用于在塊作用域中聲明時阳仔,必須與 static
或 extern
存儲期組合。
2.1.4.2. 原子類型
新增關鍵字 _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_t
和 char32_t
延蟹,通常是 typedef评矩。
2.2. 標準庫級
2.2.1. 線程
新增線程支持庫
頭文件
<threads.h>
新增線程、線程局部存儲和同步原語操作函數(shù)阱飘。
頭文件
<stdatomic.h>
新增原子操作函數(shù)斥杜。