简体   繁体   English

这是 C 语言未定义的行为吗? clang 和 GCC 的不同结果

[英]Is this a C language undefined behaviour? Different results in clang and GCC

I'm getting different results for the same code with different compilers.对于不同编译器的相同代码,我得到不同的结果。 Is this a undefined behaivour?这是未定义的行为吗?

#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);
}

The behavior is unspecified, not undefined.行为是未指定的,不是未定义的。

The C standard distinguishes these. C标准区分了这些。 C 2018 3.4.4 1 says: C 2018 3.4.4 1 说:

unspecified behavior未指定的行为

behavior, that results from the use of an unspecified value, or other behavior upon which this document provides two or more possibilities and imposes no further requirements on which is chosen in any instance行为,这是由于使用未指定的值,或本文档提供两种或多种可能性的其他行为,并且在任何情况下都不会对其施加进一步的要求

And 3.4.3 1 says: 3.4.3 1 说:

undefined behavior未定义的行为

behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this document imposes no requirements使用不可移植或错误的程序构造或错误数据时的行为,本文档对此没有强加任何要求

In some situations, when an object is both used for its value and modified, a rule in the C standard makes the behavior undefined.在某些情况下,当一个对象既用于它的值又被修改时,C 标准中的一条规则使行为未定义。 6.5 2 says: 6.5 2 说:

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.如果标量对象的副作用相对于同一标量对象的不同副作用或使用同一标量对象的值的值计算是未排序的,则行为未定义。 If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.如果一个表达式的子表达式有多个允许的排序,并且在任何排序中出现这种未排序的副作用,则行为是未定义的。

Let's see how this applies to a=a+puan_ekle(a,b);让我们看看这如何应用于a=a+puan_ekle(a,b); . . In this expression:在这个表达式中:

  1. a is modified by the a= . aa=修改。
  2. a is used in the a+ . a用于a+
  3. a is used in the arguments (a,b) . a在参数(a,b)中使用。
  4. Inside the function puan_ekle , a is modified with a=puan-5;在函数puan_ekle中, a被修改为a=puan-5; . .

The modifications are side effects—they are something that happens separately from computing the value of the expression.修改是副作用——它们是与计算表达式的值分开发生的事情。 If either of the modifications, 1 or 4, is unsequenced relative to any of the other items, the behavior is undefined.如果修改 1 或 4 中的任何一个相对于任何其他项目没有排序,则行为未定义。

Regarding 1, 6.5.16 3 says:关于 1,6.5.16 3 说:

… The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands… ...更新左操作数的存储值的副作用在左右操作数的值计算之后排序...

So 1 is sequenced after 2 and 3. Since 4 is a side effect, not a value computation, we still have to consider the relationship of 1 and 4. To resolve this, we will consider sequence points.所以1排在2和3之后。由于4是副作用,不是值计算,所以我们还是要考虑1和4的关系。要解决这个问题,我们会考虑序列点。 Per 5.1.2.3, “The presence of a sequence point between the evaluation of expressions A and B implies that every value computation and side effect associated with A is sequenced before every value computation and side effect associated with B .”根据 5.1.2.3,“表达式AB的求值之间序列点的存在意味着与A关联的每个值计算和副作用在与B关联的每个值计算和副作用之前排序。”

Next we need to know what a full expression is and that there is a sequence point after each full expression.接下来我们需要知道什么是完整表达式,以及每个完整表达式之后都有一个序列点。 6.8 4 says: 6.8 4 说:

A full expression is an expression that is not part of another expression, nor part of a declarator or abstract declarator… There is a sequence point between the evaluation of a full expression and the evaluation of the next full expression to be evaluated.完整表达式是不属于另一个表达式的表达式,也不是声明符或抽象声明符的一部分……在完整表达式的求值和要求值的下一个完整表达式的求值之间有一个序列点。

This means that every statement inside puan_ekle is or contains a full expression: puan=puan+bonus is a full expression, a=puan-5 is a full expression, bonus-- is a full expression, and the bonus in return bonus is a full expression.这意味着puan_ekle中的每个语句都是或包含一个完整的表达式: puan=puan+bonus是一个完整的表达式, a=puan-5是一个完整的表达式, bonus--是一个完整的表达式, return bonus bonus的 bonus 是一个完整的表达。 So, after a=puan-5 , there is a sequence point.所以,在a=puan-5之后,有一个序列点。

Since, for a= , the side effect of modifying a is sequenced after the value computations of the operands.因为,对于a= ,修改a的副作用在操作数的值计算之后排序。 Evaluating those operands includes calling the function, which includes its sequence points.评估这些操作数包括调用包含其序列点的函数。 So effect 4, modifying a in a=puan-5;所以效果4,修改a in a=puan-5; , must be completed before execution continues to the next statement, and hence must be completed before effect 1. So 1 and 4 are sequenced. , 必须在继续执行下一条语句之前完成,因此必须在效果 1 之前完成。所以 1 和 4 是有顺序的。

What is left is to consider effect 4 with respect to 2 and 3. 4 is sequenced after 3 because a function call is sequenced after evaluation of its arguments, per 6.5.2.2 10:剩下的就是考虑关于 2 和 3 的效果 4。4 在 3 之后排序,因为根据 6.5.2.2 10,函数调用在对其参数求值之后排序:

There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call…在函数指示符和实际参数的评估之后但在实际调用之前有一个序列点......

Now all we have left is the sequencing of 2 relative to 4. In this, there is no specification of which is first.现在我们只剩下 2 相对于 4 的排序。在此,没有说明哪个在前。 The evaluations of the operands of + are unsequenced, so, for a+puan_ekle(a,b) , a C implementation may do either a first or puan_ekle(a,b) first. +的操作数的评估是无序的,因此,对于a+puan_ekle(a,b) ,C 实现可能首先执行a first 或puan_ekle(a,b) first。 However, whichever it does first, there is a sequence point between 2 and 4:但是,无论它先做什么,在 2 和 4 之间都有一个序列点:

  • If a is evaluated first, then, before the function call, there is a sequence point (per 6.5.2.2 10, quoted above).如果首先评估a ,那么在函数调用之前,有一个序列点(根据上面引用的 6.5.2.2 10)。
  • If puan_ekle(a,b) is evaluated first, there is a sequence point after the full expression a=puan-5 .如果首先评估puan_ekle(a,b) ,则在完整表达式a=puan-5之后有一个序列点。

Thus, 2 and 4 are indeterminately sequenced .因此,2 和 4 的序列不确定 (5.1.2.3 3: “… Evaluations A and B are indeterminately sequenced when A is sequenced either before or after B , but it is unspecified which…”) But they are not unsequenced, so there is no undefined behavior. (5.1.2.3 3:“......当AB之前或之后排序时,评估AB的排序不确定,但未指定是哪个......”)但它们并非未排序,因此不存在未定义的行为。 The behavior is unspecified because there are two possibilities.行为未指定,因为有两种可能性。 The C implementation is required implement one of those two possibilities, which is different from undefined behavior, in which there would be no requirements. C 实现需要实现这两种可能性中的一种,这与没有要求的未定义行为不同。

The order pf evaluation of the operands of an additive operator is unspecified.加法运算符的操作数的求值顺序未指定。

So for example in this statement所以例如在这个声明中

a=a+puan_ekle(a,b);

one compiler can at first evaluate the value of a and then call the function puan_ekle(a,b) that has a side effect of changing a.一个编译器可以首先评估 a 的值,然后调用具有更改 a 副作用的函数 puan_ekle(a,b)。 While other compiler can at first call the function and after that get the value of a after it was changed in the function.而其他编译器可以首先调用该函数,然后在函数中更改后获取 a 的值。

So the program has undefined behavior.所以程序有未定义的行为。

If the function had no the side effect then the behavior would be well-defined independently of the order of evaluation of operands of the additive operator.如果函数没有副作用,那么行为将被明确定义,与加法运算符的操作数的评估顺序无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM