美文网首页
C 语言 99, 11 新政策

C 语言 99, 11 新政策

作者: Platanuses | 来源:发表于2023-01-04 20:05 被阅读0次

1. C99

已经是上个世纪的最后一版 C 语言,但由于谭浩强当年没有跟上,导致我们很多人也没跟上。

1.1. 语法级

1.1.1. 基本概念

1.1.1.1. 注释

C99 起支持 C++ 式的双斜杠单行注释。

1.1.1.2. 标识符

标识符中可使用由 \u\U 转义的 Unicode 字符。\u 后面为 4 位 16 进制数,\U 后面为 8 位 16 进制数,该 16 进制数表示一个 Unicode 码点,如:

int \u6211 = 42;

1.1.2. 预处理

1.1.2.1. 宏

1.1.2.1.1. 宏定义

#define 指令的形参列表尾部可使用变长参数列表 ...,使用 __VA_ARGS__ 访问变长参数,如:

#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__ 将变长参数放入一对双引号中,并用一个逗号与空格隔开(无论代码如何排版),如:

#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__,当目标环境为操作系统之下则为 1,目标环境无操作系统则为 0

1.1.3. 表达式

1.1.3.1. 字面量

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

新增字符字面量转义符 \u\U\u 后面为 4 位 16 进制数,\U 后面为 8 位 16 进制数,该 16 进制数表示一个 Unicode 码点,如 '\u6211'

新增复合字面量用以表达数组、结构体、联合体,其形式为 (类型){初始化列表},如:

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

1.1.3.2. 初始化

初始化列表中的项,新增一种可用形式 指派器列表=初始化器。其中指派器列表的形式为数组的下标表达式 [常量表达式] 或结构体、联合体的成员访问表达式 .标识符

对于数组的初始化,可使用指派器列表指定下标,后续的下标会在此指定值的基础上依次加 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 }

可用于嵌套数组,此时指派器列表可以是嵌套的数组下标表达式,用于指定当前嵌套深度下的数组下标,如:

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 } }

对于结构体的初始化,可使用指派器列表指定成员,后续的成员会依照结构体中成员声明的顺序,后面相同成员的赋值会覆盖前面,如:

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

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

可用于嵌套结构体,此时指派器列表可以是嵌套的成员访问表达式,用于指定当前嵌套深度下的成员,如:

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. 声明

声明不再限定于只能出现在块的开头。这是 C99 中谭浩强错过的最重要的一点,以致于到了二十一世纪我们许多人还遵守着这个限制。

1.1.4.1. 限定符

在函数声明中,限定符可用于数组形参的方括号内,数组形参会转化为指针形参,限定符转而修饰该指针,如:

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

新增限定符关键字 restrict 以修饰一个指针类型的左值,提示编译器在该指针的作用域内,如果经由该指针所访问的内存会被修改,则该内存仅经由该指针访问。根据此限定符,编译器能作出更大胆的优化。

1.1.5. 语句

1.1.5.1. for

for 语句的初始化子句可以是一个声明,该声明的作用域为整个循环体,包括循环体的条件表达式和迭代表达式,如:

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

1.1.6. 类型

1.1.6.1. 标量

新增至少 64 位的有符号整型 long long 和无符号整型 unsigned long long

新增算术类型关键字 _Bool,该类型的值只能是 01。通常使用头文件 stdbool.h 中定义的宏 booltruefalse。所有非零值的标量转化为 _Bool 类型后其值为 1,零值的标量转化为 _Bool 类型后其值为 0,注意这与转化为其他某种整型时的区别,特别是将其他某种整型用作布尔类型时,如:

#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. 数组

新增变长数组。声明一个数组时如果长度不是一个常量,则声明为变长数组,如:

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

变长数组的声明不能使用初始化列表。变长数组的长度在运行时确定,内存在运行时分配,长度在其生命周期内不可变。变长数组的生命周期只能在函数块内部,不能作为全局变量、结构体和联合体的成员。

变长数组作为函数形参时,在函数原型声明中,使用 * 作为数组长度,该数组同样会转化为元素类型的指针,如:

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

变长组数 T[n]sizeof 操作仍是计算整个数组的长度,即 sizeof(T) * n,但是在运行时计算,因此不再是常量。

1.1.6.3. 枚举

允许枚举声明中的最后一项的后面出现逗号。

1.1.6.4. 结构体

新增结构体柔性数组成员。柔性数组成员只能作为不完整数组类型而声明在最后,如:

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

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

结构体的初始化列表、sizeof 操作符、赋值操作符会忽略柔性数组成员。包含柔性数组成员的结构体不能作为数组元素和结构体成员。

结构体位域字段可使用 _Bool 类型,宽度只能为 1,如:

struct S {
    _Bool b: 1;
};

1.1.7. 函数

新增函数域内的预定义静态局部变量 static const char __func__[],内容为当前函数名。

1.2. 标准库级

1.2.1. 类型

头文件 <stdbool.h>

定义了宏 booltruefalsetruefalse 是整型常量 10

头文件 <stdint.h>

定义了确定长度的整型 typedef int8_tint16_tint32_tint64_tintptr_tuint8_tuint16_tuint32_tuint64_tuintptr_t 等。

1.2.2. 数值计算

新增大量数值计算函数。

参考文档

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__,当编译器不支持复数类型且标准库不包含复数类型 API 则为 1

新增宏 __STDC_NO_VLA__,当编译器不支持变长数组则为 1

2.1.3. 表达式

2.1.3.1. 字面量

新增字符字面量前缀 u,字符类型为 char16_t,通常是 UTF-16 编码。如 u'我'(char16_t)0x6211,若字符的 Unicode 码点对应的 UTF-16 编码超出单个编码单元,则依照编译器的具体实现。

新增字符字面量前缀 U,字符类型为 char32_t,通常是 UTF-32 编码。如 U'我'(char32_t)0x00006211,若字符的 Unicode 码点对应的 UTF-32 编码超出单个编码单元(这都能超过?),则依照编译器的具体实现。

新增字符串字面量前缀 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(控制表达式, 关联列表)。关联列表中的关联项由逗号分隔,关联项的形式为 类型名: 表达式default: 表达式,各表达式不能为逗号表达式。控制表达式会经历左值转换(去掉顶层的类型限定符、数组到指针、函数到指针)但不会求值,转换后的类型与各关联项中的类型名进行匹配,返回匹配到的关联项中的表达式,若无 default 项且无匹配项则编译出错。如:

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

2.1.3.3. 静态断言

新增关键字 _Static_assert 以提供静态断言,其形式为 _Static_assert(整型常量表达式, 字符串字面量),静态断言在编译时进行,当整型常量表达式的值为 0 则发生编译错误,字符串字面量会出现在错误信息中。

2.1.4. 声明

2.1.4.1. 存储期说明符

新增关键字 _Thread_local 以声明变量的存储期为新增的线程存储期。线程存储期为该线程的整个执行过程,变量在线程启动时初始化。不能用于函数声明。用于在块作用域中声明时,必须与 staticextern 存储期组合。

2.1.4.2. 原子类型

新增关键字 _Atomic 以声明变量为原子类型。

不能用于数组和函数声明,如:

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

但可声明元素为原子类型的数组,如:

_Atomic int p[2];

2.1.5. 类型

2.1.5.1. 标量

新增 16 位和 32 位无符号字符类型 char16_tchar32_t,通常是 typedef。

2.2. 标准库级

2.2.1. 线程

新增线程支持库

头文件 <threads.h>

新增线程、线程局部存储和同步原语操作函数。

头文件 <stdatomic.h>

新增原子操作函数。

参考文档

相关文章

  • C 语言 99, 11 新政策

    1. C99 已经是上个世纪的最后一版船新 C 语言,但由于谭浩强当年没有跟上,导致我们很多人也没跟上。 1.1....

  • C语言笔记(3)

    Collected Webpage: 【整理】C语言的各种版本:C89,AMD1,C99,C11 | 在路上 Co...

  • C语言的原子操作

    C语言原子操作是在C11(C11:标准是C语言标准的第三版,前一个标准版本是[C99]标准)引入的,定义在头文件 ...

  • c语言关键词 和 控制语句

    C语言关键字分类整理 C语言总览: 强类型,面向过程 简洁、灵活:32个关键字(C99标准新增5个,C11新增7个...

  • C标准库

    今天总结一下C语言标准库。 C语言标准库(C89)包含15个头文件,新的C99以及C11又定义了一些其他的库,这里...

  • 兼容性和稳定性

    C++11 与 C99 的兼容 C11 之前最新的 C标准是 1999 年制定的 C99 标准。而第一个 C++ ...

  • [C++11阅读][1-2-1]对C标准的兼容

    与C99兼容 C++11之前最新的C标准是1999年的C99标准。C++98出现在C99之前,对C99支持不好,各...

  • 「C」类型大小

    当前系统指定的类型大小 sizeof 为c语言内置运算符,以字节为单位给出指定类型的大小。C99和C11提供的%z...

  • C语言的布尔值?

    在C语言标准(C89)没有定义布尔类型,所以C语言判断真假时以0为假,非0为真。 或者 C语言标准(C99)解决了...

  • [转]10个C语言小技巧

    翻译自10 C99 tricks 10个C语言小窍门,原文提到是适用C99,其实部分技巧也适用于C89。MSVC没...

网友评论

      本文标题:C 语言 99, 11 新政策

      本文链接:https://www.haomeiwen.com/subject/yjcdcdtx.html