此代码行为是否已定义?

发布于 2024-10-28 08:36:23 字数 627 浏览 5 评论 0原文

以下代码将什么打印到控制台?

map<int,int> m;
m[0] = m.size();
printf("%d", m[0]);

可能的答案:

  1. 代码的行为未定义,因为未定义编译器首先执行哪个语句 m[0]m.size()。因此它可以打印 1 以及 0
  2. 它打印 0 因为赋值运算符的右侧首先被执行。
  3. 它打印 1,因为 operator[] 具有完整语句的最高优先级 m[0] = m.size() 。因此,会发生以下事件序列:

    • m[0] 在地图中创建一个新元素
    • m.size() 被调用,现在是 1
    • m[0] 被分配先前返回的值(通过 m.size())1
  4. 真正的答案?,我不知道^^

What does the following code print to the console?

map<int,int> m;
m[0] = m.size();
printf("%d", m[0]);

Possible answers:

  1. The behavior of the code is not defined since it is not defined which statement m[0] or m.size() is being executed first by the compiler. So it could print 1 as well as 0.
  2. It prints 0 because the right hand side of the assignment operator is executed first.
  3. It prints 1 because the operator[] has the highest priority of the complete statement m[0] = m.size(). Because of this the following sequence of events occurs:

    • m[0] creates a new element in the map
    • m.size() gets called which is now 1
    • m[0] gets assigned the previously returned (by m.size()) 1
  4. The real answer?, which is unknown to me^^

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

安人多梦 2024-11-04 08:36:23

我相信未指定是否将0或1存储在m[0]中,但这不是未定义的行为。

LHS 和 RHS 可以按任一顺序发生,但它们都是函数调用,因此它们在开始和结束处都有一个序列点。他们两个共同访问同一个对象而没有中间序列点,不存在危险。

该赋值是实际的 int 赋值,而不是具有关联序列点的函数调用,因为 operator[] 返回 T&。这有点令人担忧,但它不会修改此语句中其他任何地方访问的对象,因此这也是安全的。当然,它是在 operator[] 内进行访问的,在它初始化的地方,但是发生在从 operator[] 返回时的序列点之前,所以没关系。如果不是,m[0] = 0; 也将是未定义的!

但是,标准并未指定 operator= 操作数的求值顺序,因此调用 size() 的实际结果可能是 0 或 1,具体取决于发生哪个顺序。

不过,以下内容将是未定义的行为。它不会进行函数调用,因此没有什么可以阻止在没有插入序列点的情况下访问(在右侧)和修改(在左侧)size

int values[1];
int size = 0;

(++size, values[0] = 0) = size;
/*     fake m[0]     */  /* fake m.size() */

I believe it's unspecified whether 0 or 1 is stored in m[0], but it's not undefined behavior.

The LHS and the RHS can occur in either order, but they're both function calls, so they both have a sequence point at the start and end. There's no danger of the two of them, collectively, accessing the same object without an intervening sequence point.

The assignment is actual int assignment, not a function call with associated sequence points, since operator[] returns T&. That's briefly worrying, but it's not modifying an object that is accessed anywhere else in this statement, so that's safe too. It's accessed within operator[], of course, where it is initialized, but that occurs before the sequence point on return from operator[], so that's OK. If it wasn't, m[0] = 0; would be undefined too!

However, the order of evaluation of the operands of operator= is not specified by the standard, so the actual result of the call to size() might be 0 or 1 depending which order occurs.

The following would be undefined behavior, though. It doesn't make function calls and so there's nothing to prevent size being accessed (on the RHS) and modified (on the LHS) without an intervening sequence point:

int values[1];
int size = 0;

(++size, values[0] = 0) = size;
/*     fake m[0]     */  /* fake m.size() */
飘落散花 2024-11-04 08:36:23

确实打印 1,并且不会使用 gcc 发出警告(!)。它应该发出警告,因为它是未定义的。

operator[]operator. 的优先级均为 2,而 operator= 的优先级为 16。
这意味着明确定义了 m[0]m.size() 将在赋值之前执行。但是,没有定义哪个先执行。

It does print 1, and without raising a warning(!) using gcc. It should raise a warning because it is undefined.

The precedence class of both operator[] and operator. is 2 whereas the precedence class of operator= is 16.
This means that it is well-defined that m[0] and m.size() will be executed before the assignment. However, it is not defined which one executes first.

橙幽之幻 2024-11-04 08:36:23

在对operator []的调用和在此语句中调用clear。因此,行为应该是未定义的。

There is no sequence point between the call to operator [] and the call to clear in this statement. Consequently, the behaviour should be undefined.

北恋 2024-11-04 08:36:23

鉴于 C++17 已经差不多了,我认为值得一提的是,这段代码现在在新标准下表现出了明确定义的行为。对于 = 是对整数的内置赋值的情况:

[expr.ass]/1

赋值运算符 (=) 和复合赋值运算符
从右到左分组。全部都需要一个可修改的左值作为其左侧
操作数并返回引用左操作数的左值。结果
如果左操作数是位域,则在所有情况下都是位域。总共
在这种情况下,分配是在计算值之后排序的
右操作数和左操作数,以及在计算值之前
赋值表达式。 右操作数在左操作数之前排序
操作数。
对于不确定顺序的函数调用,
复合赋值的操作是一次评估。

这让我们只有一个选择,那就是#2。

Given that C++17 is pretty much here, I think it's worth mentioning that this code now exhibits well defined behavior under the new standard. For this case of = being the built-in assignment to an integer:

[expr.ass]/1:

The assignment operator (=) and the compound assignment operators all
group right-to-left. All require a modifiable lvalue as their left
operand and return an lvalue referring to the left operand. The result
in all cases is a bit-field if the left operand is a bit-field. In all
cases, the assignment is sequenced after the value computation of the
right and left operands, and before the value computation of the
assignment expression. The right operand is sequenced before the left
operand.
With respect to an indeterminately-sequenced function call,
the operation of a compound assignment is a single evaluation.

Which leaves us with only one option, and that is #2.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文