表达式的行为:定义还是未定义?

Pra*_*rav 9 c c++ undefined-behavior sequence-points

我有以下代码

int m[4]={1,2,3,4}, *y; 
y=m; 
*y = f(y++); // Expression A
Run Code Online (Sandbox Code Playgroud)

我的朋友告诉我,Expression A行为定义明确,但我不确定他是否正确.

根据他的功能f()介绍sequence point介于两者之间,因而行为定义明确.

有人请澄清一下.

PS:我知道我们不应该为实际目的编写这样的代码.这只是为了学习的目的.:)

Jam*_*lis 15

充其量,有问题的代码具有未指定的行为.对于赋值运算符,"操作数的评估顺序未指定"(C99§6.5.16/ 4).

如果首先计算左操作数,则结果f(y++)将存储在中m[0].如果首先计算右操作数,则结果将存储在m[1].

至于行为是否未定义,相关段落是:

在前一个和下一个序列点之间,一个对象的存储值最多只能通过表达式的评估来修改一次.此外,先前的值应只读以确定要存储的值(C99§6.5/ 2).

如果首先评估左侧,那么我们会违反第二句,因为排序是:

  1. y在左侧读取值以取消引用它
  2. y在右侧读取值以增加它
  3. 在评估函数的参数之后有一个序列点(因此,副作用y++已完成并被y写入)

在步骤1中,y读取"先前值" 但是出于"确定要存储的值"之外的目的.因此,行为确实未定义,因为一个有效的评估顺序会产生未定义的行为.

  • +1.我认为它甚至是未定义的,因为对`*y`和`y ++'的评价是不确定的,后者会对'y`产生副作用. (2认同)

AnT*_*AnT 13

你对引入序列点的函数调用是绝对正确的.但是,该序列点不能保存您的情况.

首先考虑这个简单的例子

i = some_function(i++);
Run Code Online (Sandbox Code Playgroud)

有效吗?是的.为什么?它是有效的,因为函数引入的序列点(您正在讨论的序列点)将两个i彼此的修改分开,从而使代码有效.没有评估此表达式的顺序会导致在i没有插入序列点的情况下被修改两次.

但是,让我们回到您的变体

*y = f(y++);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,序列点也存在.但是,该语言不保证=运算符的求值顺序(意思是:语言不能保证首先评估赋值运算符的哪个操作数:左或右).编译器很可能会评估左侧的first(*y),函数参数second(y++),然后调用该函数然后执行实际的赋值.在这种潜在的情况下,前两个步骤 - 读取y和修改y- 不会被序列点分开.因此,行为是不确定的.