这是未定义的行为吗

Posted

技术标签:

【中文标题】这是未定义的行为吗【英文标题】:Is this undefined behavior 【发布时间】:2020-06-15 08:36:25 【问题描述】:

据我了解,这个程序应该有未定义的行为。

#include <stdio.h>

int main()

   int a = 3, b = 3, c = 10, d = 20;
   int e = (a++ * ++b)-((c / b) * a) + d;
   printf("%d", e)  ;

   return 0;

C99 标准 §6.5 ¶2 说

在前一个序列点和下一个序列点之间,一个对象应该有它的 表达式的评估最多修改一次存储的值。 此外,应仅读取先验值以确定该值 存储起来。

因此,在定义 'e'ab 的行中,不仅要确定要在 ab 中存储什么,还要计算表达式 ((c / b) * a)

但是,即使使用-Wsequence-point warning,gcc 也不会发出警告。

我在这里错过了什么?

【问题讨论】:

gcc --version 对于此类问题总是一个好主意。 @rici:ideone.com/NbV30F。那是 gcc 8.3 可能是 ideone 的问题,或者它没有使用您认为的警告选项。 Godbolt compiler explorer 按预期显示警告,我的本地 gcc 9.2.1 也是如此。 @nate tio.run 也是如此:(gcc 8.3.1) tio.run/##NYzLDoIwEEX3/YobjElrqaIuUX/… 我用 GCC 版本 7.2.0、7.3.0、8.1.0、8.2.0、8.3.0、9.1.0 和 9.2.0 测试,每一个都报告了-Werror=sequence-point(错误因为我也用过-Werror)。这是在运行 macOS Mojave 10.14.6 的 Mac 上测试的。我在 2017 年 11 月至 2019 年 8 月之间构建了编译器,因此它们并非都构建在 Mojave 之上。 【参考方案1】:

使用 clang 编译器(版本 - clang-1001.0.46.4)编译时,收到以下警告:

   p.c:6:19: warning: unsequenced modification and access to 'b' [-Wunsequenced]
       int e = (a++ * ++b)-((c / b) * a) + d;
                        ^        ~
   p.c:6:14: warning: unsequenced modification and access to 'a' [-Wunsequenced]
       int e = (a++ * ++b)-((c / b) * a) + d;
                ^                     ~

来自 C11 标准#6.5p2 [强调添加]

2 如果标量对象的副作用相对于同一标量对象的不同副作用或使用同一标量对象的值的值计算是无序的,则行为未定义。如果一个表达式的子表达式有多个允许的排序,则如果在任何排序中出现这种未排序的副作用,则行为是未定义的。84)

表达式调用未定义的行为。


编辑:

带有gcc 标记的问题,因此为了完整回答,下面是使用带有-Wall 选项的gcc 编译器编译时的输出:

p.c:6:19: warning: operation on 'b' may be undefined [-Wsequence-point]
    int e = (a++ * ++b)-((c / b) * a) + d;
                   ^
p.c:6:14: warning: operation on 'a' may be undefined [-Wsequence-point]
    int e = (a++ * ++b)-((c / b) * a) + d;
              ^

请注意,如果我们在编译时没有为gcc 指定任何选项(如-Wall-Wsequence-point),则不会针对相关表达式给出任何警告消息,但clang 并非如此编译器。

【讨论】:

以上是关于这是未定义的行为吗的主要内容,如果未能解决你的问题,请参考以下文章

是否缺少删除未定义的行为? [复制]

未定义的行为和序列点

为啥在已删除指针上调用非虚拟成员函数是未定义的行为?

是否将地图尺寸作为值插入地图未定义的行为?

v-for 和自定义组件的未定义行为

虚拟析构函数和未定义的行为