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

Rof*_*er4 22 c c++ undefined-behavior

拿这三个C代码片段:

1) a = b + a++
2) a = b + a; a++
3) a = b + a, a++
Run Code Online (Sandbox Code Playgroud)

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

具体来说,我正在考虑这个问题free(foo), foo = bar.这基本上与上述问题相同.我可以确定foo在重新分配之前被释放,或者这是一个明确的序列点问题吗?

我知道这两个例子都是毫无意义的,只使用分号并完成它就更有意义了.我只是出于好奇而问.

dbu*_*ush 38

案例3定义明确.

关于逗号运算符的C标准的 6.5.17节,说明如下:

2逗号运算符的左操作数被计算为void表达式; 它的评估与右操作数之间存在一个序列点. 然后评估右操作数; 结果有它的类型和价值

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

用逗号分隔的一对表达式从左到右进行评估; 左表达式是丢弃值表达式. 在与右表达式相关联的每个值计算和副作用之前,对与左表达式相关联的每个值计算和副作用进行排序.结果的类型和值是右操作数的类型和值; 结果与右操作数具有相同的值类别,如果右操作数是glvalue和位域,则是一个位域.

由于序列点,a = b + a保证a++在表达式之前完全评估a = b + a, a++.

关于free(foo), foo = bar,这也保证了foo在分配新值之前免费.

  • @MarkRansom正确.逗号运算符的优先级低于赋值,加法运算符的优先级高于赋值. (3认同)
  • @AjayBrahmakshatriya我本可以自己查一下,但我想要一个与答案相关的解释.这在直觉上并不明显. (3认同)
  • 我的*猜*是逗号的优先级较低,所以像'for(Node*curr = head,prev = NULL; curr; prev = curr,curr = curr-> next)`运行良好 (3认同)

Lun*_*din 11

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

首先,运算符优先级使表达式等效(a = (b+a)), a++;,其中+优先级最高,后跟=依次为,.逗号运算符包括其左右操作数的计算之间的序列点.所以代码是无意义的,完全等同于:

a = b + a;
a++;
Run Code Online (Sandbox Code Playgroud)

这当然是明确定义的.


如果我们改为编写a = (b + a, a++);,那么逗号运算符中的序列点将不会保存当天.因为那时表达式就等于

(void)(b + a);
a = a++;
Run Code Online (Sandbox Code Playgroud)
  • 在C和C++ 14或更早版本中,a = a++未经测序,(见C11 6.5.16/3).这意味着这是未定义的行为(Per C11 6.5/2).请注意,C++ 11和C++ 14的配方错误且含糊不清.
  • 在C++ 17或更高版本中,运算=符的操作数从右到左排序,这仍然是明确定义的.

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

  • @Lundin - 这不是实现它的问题,而是关心它的问题.为什么有人**决定**写'a = a ++`?即使它定义得很好,为什么它会在代码审查中存活下来? (2认同)
  • @PeteBecker这是完全相同的事情,因为正如答案中所提到的那样,问题是对赋值运算符的操作数的求值没有排序.无论操作数恰好是什么,任何对赋值运算符的使用都是如此. (2认同)