图源: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; }