遵从性和行为
1. 为什么先讲行为分类
C 语言当中的行为不只分为“对”或“错”。它会区分“好”、“实现决定”、“结果不唯一”、“完全无保证”等多种情况。
2. 常见行为类别
2.1 实现定义行为
标准要求实现给出一种明确行为,并在文档中说明。例如 char 默认是有符号还是无符号,属于实现定义行为。工程上可以使用这类能力,但要通过文档或编译选项把前提写清楚。
2.2 未指明行为
标准允许出现若干种结果,但不要求实现每次都做同样选择。例如某些表达式中子表达式的求值先后次序并未固定。代码如果依赖某一种求值次序,就会在不同编译器或不同优化级别下表现不一致。
2.3 未定义行为
一旦触发,标准不再提供任何结果保证。越界访问、悬垂指针解引用、对同一标量对象进行无序修改都属于这一类。它最危险的地方在于:有时”看起来能跑”,但平台或优化变化后会立即失效。
存在未定义行为的程序是错误的,编写代码时要规避未定义行为。
c
#include <stdio.h>
int main(void) {
int a[5] = {1, 2, 3, 4, 5};
printf(“%d\n”, a[10]); /* 越界访问:未定义行为 */
return 0;
}1
2
3
4
5
6
7
2
3
4
5
6
7
这里 a[10] 越过了数组的有效下标范围(最大为 a[4]),标准对该访问的结果不作任何保证。
3. 遵从性 (Conformance)
遵从实现是指满足标准约束的编译器和库实现;遵从程序是指只依赖标准保证语义、在约束内运行的程序。项目代码中常说“严格遵从”,本质是减少实现细节依赖,让代码在更多平台上稳定工作。
4. 使用建议
把编译警告当成强反馈,编译的时候开启 -Wall -Wextra -Wpedantic 等参数(这一点在本教程配套的 VS Code 扩展当中已经做到);对实现定义行为补充注释与构建约束;把可能触发未定义行为的写法改写为可证明正确的等价表达式。