C 参考手册
- C 语言
- C 的历史
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 静态断言
- 字符常量
- 函数声明
- 函数定义
- 转义序列
- 翻译阶段
- 标识符
- 作用域
- 生存期
- 查找与命名空间
- ASCII 码表
- 类型
- 遵从性
- 算术类型
- restrict 类型限定符
- 类型
- 对象与对齐
- 主函数
- 未定义行为
- 内存模型
- if 语句
- switch 语句
- for 循环
- while 循环
- do-while 循环
- continue语句
- break 语句
- goto语句
- return 语句
- 值类别
- 求值顺序
- 整数常量
- 浮点常量
- 字符串字面量
- 复合字面量
- 常量表达式
- 隐式转换
- 成员访问运算符
- 逻辑运算符
- 比较运算符
- 算术运算符
- 赋值运算符
- 自增/自减运算符
- 其他运算符
- sizeof 运算符
- _Alignof 运算符
- 转型运算符
- C 运算符优先级
- 泛型选择
- 标量初始化
- 数组初始化
- 结构体与联合体初始化
- 指针声明
- 数组声明
- 枚举
- 存储类指定符
- const 类型限定符
- volatile 类型限定符
- 结构体声明
- 联合体声明
- 位域
- _Alignas
- typedef 声明
- 原子类型
- 外部及试探性定义
- inline 函数指定符
- _Noreturn 函数指定符
- 变长参数
- 内联汇编
- 可分析性
- 替用运算符及记号
- C 关键词
- 预处理器
- C 标准库头文件
- 类型支持
- 程序支持工具
- 变参数函数
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 算法
- 数值
- 文件输入/输出
- 本地化支持
- 原子操作库
- 线程支持库
- 实验性 C 标准库
- 有用的资源
- 符号索引
- 注释
求值顺序
除下列标出者,任意 C 运算符的运算数求值顺序,包括函数调用表达式的函数参数求值顺序,及任何表达式的子表达式求值顺序都是未指定的。编译器会以任意顺序对其求值,而且在同一表达式被再度求值时可选用另一种顺序。
C 中没有从左到右或从右到左求值的概念,这不会与运算符的从左到右或从右到左结合性混淆:表达式 f1() + f2() + f3()
被分析成 (f1() + f2()) + f3()
,因为 operator+ 的从左到右结合性,但运行时对 f3
的函数调用可以最先、最后,或在 f1()
与 f2()
之间求值。
定义
求值
对于每个表达式或子表达式,有二种求值为编译器进行(两者都是可选的):
- 值计算( value computation ):计算表达式所返回的值。这可以涉及到确定对象身份(左值求值)或读取之前赋给对象的值(右值求值)
- 副效应( side effect ):访问(读或写)以 volatile 左值指代的对象、修改(写)对象、原子同步 (C11 起)、修改文件、修改浮点环境(若支持)或调用进行上述操作的函数。
若表达式不产生副效应,且编译器能确定其值不被使用,则表达式可以不求值。
排序
“先序于( sequenced before )”是一种同一线程内求值的不对称、传递性、成对的关系(若引入原子类型和内存屏障,则它可以扩展到线程间)。
- 若在子表达式 E1 和 E2 间存在序列点( sequence point ),则 E1 的值计算和副效应都先序于 E2 的所有值计算和副效应
|
(C11 起) |
规则
1) 在所有函数参数和函数指代器的求值后,实际调用函数前,有一个序列点。
2) 在下例二元运算符的第一(左)运算数求值后,第二(右)运算数求值前,有一个序列点:
&&
(逻辑与)、 ||
(逻辑或),及 ,
(逗号)。3) 在条件运算符
?:
的第一(左)运算数求值后,第二或第三运算数(无论何者被求值)前,有一个序列点。4) 在完整表达式(非子表达式的表达式:典型的是以分号为结尾者或 if/switch/while/do 的控制语句)的求值后,下个完整表达式前,有一个序列点。
5) 在完整声明器的结尾,有一个序列点。
6) 在紧接库函数返回前,有一个序列点。
|
(C99 起) |
9) 属于运算符的运算数的值计算(但非副效应)先序于运算符的值计算(但非其副效应)。
10) 直接赋值运算符与所有复合赋值运算符的副效应(修改左参数)后序于左右参数的值计算(但非其副效应)。
11) 后自增和后自减运算符的值计算先序于其副效应。
12) 既非先序于亦非后序于另一函数调用的函数调用是非确定顺序的(构成不同函数调用的 CPU 指令不可能交错,即使函数被内联)。
13) 在初始化器列表表达式中,所有求值都是非确定顺序的
14) 考虑到非确定顺序的函数调用,复合赋值运算符,及自增减运算符的前后缀形式都是单独求值。
|
(C11 起) |
未定义行为
1) 若对一个标量对象的副效应与另一个对同一标量对象的副效应相对无顺序,则行为未定义。
i = ++i + i++; // 未定义行为 i = i++ + 1; // 未定义行为 f(++i, ++i); // 未定义行为 f(i = -1, i = -1); // 未定义行为
2) 若一个标量对象上的副效应与另一个使用同一标量对象之值的值计算相对无顺序,则行为未定义。
f(i, i++); // 未定义行为 a[i] = i++; // 未定义行为
3) 只要至少一个子表达式的排序容许这种无顺序副效应,就应用上述规则。
参阅
运算符优先级,定义自源代码表示构建表达式的方式。