2

对于使用不同编译器的相同代码,我得到不同的结果。这是未定义的行为吗?

#include <stdio.h>
int a;
int b=10;
int puan_ekle(int puan, int bonus){
    puan=puan+bonus;
    a=puan-5;
    bonus--;
    return bonus;
}
int main(){
    a=23;
    printf("Result1 %d \n", a);
    a=a+puan_ekle(a,b);
    printf("Result2 %d \n", a);
    a=a+puan_ekle(a,b);
    printf("Result3 %d \n", a);
}
4

2 回答 2

6

行为是未指定的,而不是未定义的。

C 标准区分了这些。C 2018 3.4.4 1 说:

未指明的行为

行为,是由于使用未指定的值,或本文档提供两种或多种可能性的其他行为,并且在任何情况下都没有对其选择的进一步要求

3.4.3 1 说:

未定义的行为

行为,在使用不可移植或错误程序构造或错误数据时,本文档对此没有任何要求

在某些情况下,当一个对象既用于其值又被修改时,C 标准中的一条规则使行为未定义。6.5 2 说:

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

让我们看看这如何适用于a=a+puan_ekle(a,b);. 在这个表达式中:

  1. a由 修改a=
  2. a用于a+.
  3. a在参数中使用(a,b)
  4. 函数里面puan_eklea是用 修改的a=puan-5;

修改是副作用——它们与计算表达式的值是分开发生的。如果修改 1 或 4 中的任何一个相对于任何其他项未排序,则行为未定义。

关于 1,6.5.16 3 说:

…更新左操作数的存储值的副作用是在左操作数和右操作数的值计算之后排序的……</p>

所以 1 在 2 和 3 之后排序。由于 4 是副作用,而不是值计算,我们仍然必须考虑 1 和 4 的关系。为了解决这个问题,我们将考虑序列点。根据 5.1.2.3,“在表达式AB的求值之间存在序列点意味着与A关联的每个值计算和副作用都在与B关联的每个值计算和副作用之前排序。”</p>

接下来我们需要知道什么是完整表达式,以及每个完整表达式后面都有一个序列点。6.8 4 说:

完整表达式是不属于另一个表达式的表达式,也不是声明符或抽象声明符的一部分……在完整表达式的评估和要评估的下一个完整表达式的评估之间存在一个序列点。

这意味着里面的每个语句puan_ekle都是或包含一个完整的表达式:puan=puan+bonus是一个完整的表达式,a=puan-5是一个完整的表达式,bonus--是一个完整的表达式,而bonusinreturn bonus是一个完整的表达式。所以,在 之后a=puan-5,有一个序列点。

因为,对于a=,修改的副作用a是在操作数的值计算之后排序的。评估这些操作数包括调用函数,其中包括其序列点。所以 effect 4 修改aina=puan-5;必须在执行到下一条语句之前完成,因此必须在 effect 1 之前完成。所以 1 和 4 是有序的。

剩下的就是考虑关于 2 和 3 的效果 4。根据 6.5.2.2 10,函数调用在评估其参数之后排序,因为函数调用在 3 之后排序:

在函数指示符和实际参数的评估之后但在实际调用之前有一个序列点……</p>

现在我们剩下的就是 2 相对于 4 的排序。在此,没有指定哪个是第一个。的操作数的评估+是无序的,因此,对于a+puan_ekle(a,b),C 实现可以a先执行,也可以先执行puan_ekle(a,b)。但是,无论它先做什么,在 2 和 4 之间都有一个序列点:

  • 如果a首先评估,则在函数调用之前,有一个序列点(根据 6.5.2.2 10,上面引用)。
  • 如果puan_ekle(a,b)首先评估,则在完整表达式之后有一个序列点a=puan-5

因此,2 和 4 是不确定排序的。(5.1.2.3 3:“……当 A 在 B 之前或之后排序时,评估AB排序不确定,但未指定哪个……”)但它们不是未排序的,因此没有未定义的行为。该行为未指定,因为有两种可能性。C 实现需要实现这两种可能性中的一种,这与未定义的行为不同,其中没有要求。

于 2019-12-01T16:31:04.680 回答
3

未指定加法运算符的操作数的顺序 pf 评估。

所以例如在这个声明中

a=a+puan_ekle(a,b);

一个编译器可以首先评估 a 的值,然后调用具有改变 a 的副作用的函数 puan_ekle(a,b)。而其他编译器可以首先调用该函数,然后在函数中更改它后获取 a 的值。

所以程序有未定义的行为。

如果函数没有副作用,那么行为将被明确定义,而与加法运算符的操作数的评估顺序无关。

于 2019-12-01T13:30:30.283 回答