C中的此代码是否属于未定义行为类别?

时间:2017-08-21 18:03:14

标签: c arrays language-lawyer increment undefined-behavior

a是一个数组,foo是一个函数,iint

a[++i] = foo(a[i-1], a[i]);

上面的代码是否有未定义的行为

数组索引++ii-1i保证在数组范围内。

4 个答案:

答案 0 :(得分:7)

行为是未定义的,但这不是因为在两个序列点之间对同一对象进行了两次修改,但它是UB,因为相对于{{1}的值计算,i的副作用是不相等的}和a[i-1]使用a[i]

§6.5-P(2):

  

如果对标量对象的副作用相对于对同一个标量对象产生不同的副作用或使用相同标量对象的值进行值计算时未按顺序排序,则行为未定义。如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的。 84)

例如,表达式

i

调用未定义的行为。

也是如此
a[i++] = i;

答案 1 :(得分:5)

根据C标准(6.5.16分配运营商):

  

语义

     

3 ...操作数的评估未经测试

因此这句话

a[++i] = foo(a[i-1], a[i]);

导致未定义的行为。

答案 2 :(得分:4)

是的,这是未定义的行为。

不是因为不允许对2个序列点之间的相同变量进行读写,而是因为

的规则
  

在任意两个序列点之间,变量的所有读取都应该直接用于计算写入同一变量的结果。

此处对i的写入为i++。对同一变量的读取在参数中。 虽然函数调用是一个序列点,但赋值不是。因此,在评估RHS之前,可以对阵列索引进行评估。

i中的foo(a[i-1], a[i])上的读取不直接导致写入,因此它是UB。

C99标准的相关部分是6.5表达式,§2

  

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。 此外,先前的值应该只读以确定要存储的值。

(强调我的)

答案 3 :(得分:3)

行为未定义,因为表达式a[++i]a[i-1]a[i]相对于彼此未被排序。

章节和经文:

6.5表达式
...
2 如果相对于不同的副作用,对标量物体的副作用是无效的 在相同的标量对象上或使用相同标量的值进行值计算 对象,行为未定义。如果有多个允许的排序 表达式的子表达式,如果这样一个未经测序的一方,则行为是不确定的 效果发生在任何排序中。 84)
...
6.5.2.2函数调用
...
10 在评估函数指示符和实际值之后有一个序列点 参数但在实际调用之前。调用函数中的每个评估(包括 其他函数调用)在其他方式之前或之后没有特别排序 被调用函数体的执行是相对于不确定地排序的 执行被调用的函数。 94)
...
6.5.16分配运营商
...
3赋值运算符将值存储在左操作数指定的对象中。一个 赋值表达式具有赋值后的左操作数的值, 111)但不是 一个左值。赋值表达式的类型是左操作数将具有的类型 在左值转换后。更新左操作数的存储值的副作用是 在左右操作数的值计算之后排序。 评估 操作数没有排序。 84)此段落呈现未定义的语句表达式,例如
    i = ++i + 1;
    a[i++] = i;
允许的同时
    i = i + 1;
    a[i] = i;
...
94)换句话说,功能执行不会相互“交错” ...
111)允许实现读取对象以确定值,但不是必需的 当对象具有volatile限定类型时。

C 2011 Online Draft