在 C 和 C++ 中,使用逗号运算符的表达式是否未定义"a = b, ++a;"?

In C and C++, is an expression using the comma operator like "a = b, ++a;" undefined?

本文关键字:未定义 是否 ++a 表达式 运算符 C++      更新时间:2023-10-16

取以下三个C代码片段:

1) a = b + a++
2) a = b + a; a++
3) a = b + a, a++

每个人都知道示例 1 是一件非常糟糕的事情,并且显然调用了未定义的行为。示例 2 没有问题。我的问题是关于示例 3。逗号运算符在这种表达式中是否像分号一样工作?2 和 3 是等价的还是 3 和 1 一样未定义?

具体来说,我正在考虑关于类似free(foo), foo = bar的事情.这基本上与上述问题相同。我能否确定foo在重新分配之前已释放,或者这是一个明确的序列点问题?

我知道这两个例子在很大程度上都是毫无意义的,只使用分号并完成它更有意义。我只是出于好奇而问。

情况 3 定义良好。

首先,让我们看看表达式是如何解析的:

a = b + a, a++

逗号运算符,的优先级最低,其次是赋值运算符=、加法运算符+和后缀运算符++。 因此,使用隐式括号将其解析为:

(a = (b + a)), (a++)

从这里开始,C 标准中关于逗号运算符,的第 6.5.17 节如下:

2逗号运算符的左操作数被计算为空表达式;其之间有一个序列点 评估和正确操作数的评估。然后右边 计算操作数;结果有其类型和值

C++11 标准的第 5.14 p1 节具有类似的语言:

用逗号分隔的一对表达式从左到右计算; 左侧表达式是丢弃值表达式。与左边相关的每个值计算和副作用 表达式在每个值计算和副作用之前排序 与正确的表达相关联。结果的类型和值 是正确操作数的类型和值;结果是一样的 值类别作为其右操作数,如果其右操作数,则为位字段 操作数是一个 glvalue 和一个位字段。

由于序列点,a = b + a保证在表达式a = b + a, a++a++之前完全计算。

关于free(foo), foo = bar,这也保证了foo在分配新值之前是自由的。

a = b + a, a++;是明确定义的,但a = (b + a, a++);可以是未定义的。

首先,运算符优先级使表达式等价于(a = (b+a)), a++;,其中+具有最高优先级,其次是=,后跟,。逗号运算符在其左操作数和右操作数的计算之间包含一个序列点。所以代码无趣地完全等同于:

a = b + a;
a++;

这当然是明确定义的。


如果我们改写a = (b + a, a++);,那么逗号运算符中的序列点将无法挽救这一天。因为那时表达式将等效于

(void)(b + a);
a = a++;
  • 在C和C++14或更早版本中,a = a++是未排序的(见C11 6.5.16/3(。这意味着这是未定义的行为(根据 C11 6.5/2(。请注意,C++11 和 C++14 的表述不当且模棱两可。
  • 在 C++17 或更高版本中,=运算符的操作数从右到左排序,这仍然是明确定义的。

所有这些都假设没有发生C++运算符重载。在这种情况下,将评估重载运算符函数的参数,在调用函数之前发生一个序列点,从那里发生的情况取决于该函数的内部结构。