图源:Anmi - 水族館 83088427
虽然没有固定标准,但一般将C99之后的C语言标准称为“现代C语言”;目前的最新标准为C23;
C23标准中值得注意的变化 以下是一部分我认为比较重要的变化,完整变化列表可以参阅 https://en.cppreference.com/w/c/23 或ISO标准文档。
替代
<assert.h>中的static_assert()宏被替代,变成了static_assert关键字;
<threads.h>中的thread_local()宏被替代,变成了thread_local关键字;
<time.h>中的ctime()函数弃用,请使用ctime_s()替代;
<time.h>中的asctime()函数弃用,请使用asctime_s()替代;
<stdnoreturn.h>与_Noreturn标识符均弃用;
<stdalign.h>中的alignas()和alignof()宏被弃用,请直接使用_Alignas和_Alignof关键字;
新增
C23新增了三个十进制浮点数数据类型(关键字):_Decimal32、_Decimal64和_Decimal128,对应的后缀是DF、DD和DL。它们的最大值分别如下:
1 2 3 DEC32_MAX 9 .999999 E96DFDEC64_MAX 9 .999999999999999 E384DDDEC128_MAX 9 .999999999999999999999999999999999 E6144DL
C23可以使用二进制字面量了,使用0b或者0B开头,例如:
C23添加了bool、true、false三个关键字,可以像C++一样定义布尔类型了:
C23新加了nullptr关键字,它是nullptr_t类型的,可以被强制转换为任意指针类型(传统空指针)及布尔类型(可用于逻辑判断):
1 2 3 4 5 6 7 8 9 10 11 void func (int a, nullptr_t b) { } func(10 , nullptr); int *a = nullptr;if (!a) { printf ("A is nullptr" ); }
C23添加了双括号属性(Attributes)了,常用的比如:
[[deprecated]]
[[nodiscard]]
[[noreturn]]
[[maybe_unused]]
C23新加了一些预编译命令,常用的比如:
#elifdef
#elifndef
#warning:让编译器抛出警告
#embed:让编译器直接内嵌二进制数据
1 2 3 static const char song[] = { #embed <music.wav> };
1 2 3 4 5 int a[5 ] = { 0 };int a[5 ] = {};int a[5 ] = { 0 , 0 , 0 , 0 , 0 };
C23的宏支持__VA_OPT__了,能更方便地解决使用宏时末尾符号的问题;
C23给<stdio.h>中的printf()函数添加了%b和%B支持,能像打印16进制(%x %X)一样直接打印二进制数据了;scanf()也增加了%b支持;
C23给<string.h>增加了memccpy(),与memcpy()类似但遇到某个特定值时会立刻停止复制;
C23给<string.h>增加了strdup()与strndup(),用于复制出一个新的(部分)字符串;
C23引入了函数定义时的匿名参数,如果一个参数因为某种原因必须被传递但却不被使用,就可以把它设置为匿名参数:
1 2 3 4 int func (int num, char *) { return num1 + 5 ; }
C23引入了constexpr支持,可以定义编译期变量 了;
C23将auto关键字的语义进行了修改。auto原本作为Storage class specifier时极少使用,因此auto在C23里变为了自动类型推导关键字 。是的,C语言也可以使用auto推导了:
1 2 3 4 5 6 const char * func () { return "Hello!" ; } auto fRet = func();
C23增加了对“X位整数”的支持,类型关键字为_BitInt(),编程时可以自由指定整数是几位。类型对应的字面量后缀是wb/WB和uwb/UWB例如:
1 2 unsigned _BitInt (12 ) a = 0u wb;
<uchar.h>中加入类型char8_t,存储UTF-8字符。类型对应的字面量前缀是u8。例如:
1 char8_t srt[] = u8"你好!" ;
C23允许给enum(枚举类型)指定类型了,如果不指定类型则默认为int。例如:
1 2 3 4 enum flags : unsigned long { err1 = 0xC OOOFFFF; err2 = 0xC0010000 ; }
1 2 int a = 10 ;typeof (a) b = 5 ;
<stdarg.h>中用于实现可变函数参数的宏 va_start不再强制需要第二个参数,拥有可变参数的函数也不再需要至少一个有名参数了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int add_nums_C23 (...) { int result = 0 ; va_list args; va_start(args); int count = va_arg(args, int ); for (int i = 0 ; i < count; ++i) { result += va_arg(args, int ); } va_end(args); return result; }
删除
<stdlib.h>中的realloc()不再支持size为0的情况,改为未定义行为;
C23取消了对三字母词(Trigraph)的支持。三字母词是一种转义字符,由??开头。例如在字面量中使用??)代替]。
C23规定整数必须使用补码存储,不应再使用原码和反码;
C23决定不再支持K&R格式。K&R格式是一种老式C语言写法,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func1(); func1(a, b, c) int a; char * b; int c; { d = a + c; return d; } int func1 (int a, char * b, int c) ;int func1 (int a, char * b, int c) { int d = a + c; return d; }