【问题标题】:Comma separated expressions in printf() gives an unexpected resultprintf() 中的逗号分隔表达式给出了意外的结果
【发布时间】:2020-12-05 12:33:11
【问题描述】:

如果太显眼,我很抱歉,但我在这里有点晦涩。

我写了以下代码:

#include <stdio.h>
int main() {
    int a = 8 , b = 7 ;
    int temp;
    printf("%d\n", temp=a,a=b,b=temp);
    return 0;
}

它会发出警告 输出:0

根据我的说法,输出应该是8,因为我已经尝试过这种代码printf("%d",a,b); 它打印出a的值,这使我得出结论,第一个表达式将打印出警告 格式参数过多
所以,在我的情况下,temp=a 必须是答案,因此应该打印成为8 的 temp 值。

请解释一下,我哪里错了?

提前感谢您提供的任何帮助

【问题讨论】:

    标签: c printf assignment-operator


    【解决方案1】:

    H.S. 的回答在 C 语言规范中未指定参数的评估顺序是正确的。可以按任何顺序评估参数。

    求值顺序通常是向后的(从最右边的参数到第一个参数)是相对常见的。这是因为堆栈上参数排序的细节。

    我们可以推断出参数是这样计算的:

    1. b = temp :: temp 未定义,但可能为零,因此b 可能为零。
    2. a = b :: 如果 b 为零,那么 a 现在为零。
    3. temp = a :: temp 被分配了 a 的值,这可能是零,但可能是 temp 的未初始化值。这是printf 用来格式化%d 的参数。没有使用额外的参数(并且可能会产生警告)
    4. "%d\n" :: 此处无需评估,只是一个静态字符串。

    虽然输出可以是任何东西(包括鼻恶魔),但这有助于解释为什么输出经常是0

    【讨论】:

    • 求值顺序未指定,不是未定义
    • @abelenky 您说评估顺序未指定/未定义,但随后 U 描述了程序的流程。那么,我们可以像从最右边的参数移动到第一个参数那样总是这样做吗?
    • 这没有抓住重点。评估 od 参数是 unsequenced 彼此的,并且修改和读取相同的变量会给我们带来麻烦。
    • “比较普遍的是,评估的顺序通常是向后的” 这是正确的:顺序可以是任何顺序,但它是经常倒退。您不能依赖向后的行为,但它确实有助于解释观察到的结果。
    【解决方案2】:

    来自Order of evaluation[强调]

    任何 C 运算符的操作数的求值顺序,包括函数调用表达式中函数参数的求值顺序,任何表达式中子表达式的求值顺序是未指定(除非下文另有说明)。编译器会以任意顺序计算它们,并且当再次计算相同的表达式时可能会选择另一个顺序。

    由于未指定函数参数的评估顺序。您传递给printf() 的参数包括对同一变量的读取和写入,并且没有序列点,这将导致undefined behaviour

    来自 C 标准#6.5p2

    2 如果标量对象的副作用相对于同一标量对象的不同副作用或使用同一标量对象的值的值计算是未排序的,则行为未定义...


    编辑

    如果您对逗号作为运算符和逗号作为分隔符感到困惑,那么逗号是函数参数列表中的分隔符。

    作为参考,引用 C 标准#6.5.17p3 中的示例

    3 示例 如语法所示,逗号运算符(如本小节所述)不能出现在使用 逗号分隔列表中的项目(例如函数的参数或初始化程序列表)的上下文中. 另一方面,在这种情况下,它可以用在带括号的表达式或条件运算符的第二个表达式中。在函数调用中

          f(a, (t=3, t+2), c)
    

    该函数有三个参数,第二个的值为 5。

    在上面的例子中:

         Here comma is operator, so the expression will be evaluated to 5 (3+2)
             |
             | 
    f(a, (t=3, t+2), c)
       |           |
       |           |
       -------------
            |
       here, comma is separator
    

    【讨论】:

    • 很抱歉一直坚持这一点,但您说评估顺序未指定。每个运营商都是这样吗?就像我这样写(++a)&&(--a)&&(a++)。这也会产生未定义的行为吗?
    • 是但不是,&amp;&amp; 是强制严格排序的那些之一,因此 lhs 的副作用将在评估 rhs 之前解决,如果它甚至被评估...
    • 未指定的评估不是全部,而是它们也是未排序的事实。
    • @VinayYadav 检查随答案提供的“评估顺序”链接。在该链接中,浏览“订购”和“规则”部分。
    • 标题显示提问者认为赋值是“逗号分隔的表达式”,意思是使用 C 的逗号运算符 (C 2018 6.5.17) 的表达式。它们不是,而且这个答案无法纠正这种误解或解释为什么它们不在逗号分隔的表达式中。
    【解决方案3】:

    printf("%d\n", temp=a,a=b,b=temp); 中没有逗号运算符。在函数调用的参数列表中,逗号分隔参数。它们不代表逗号运算符。

    因此,在printf("%d\n", temp=a,a=b,b=temp); 中,格式字符串后面有三个单独的表达式:temp=aa=bb=temp

    C 标准没有指定函数参数甚至部分函数参数的计算顺序。这会导致您的代码与另一条规则发生冲突,该规则说,如果您以无序的方式修改和使用对象,则行为不是由 C 标准定义的。比如a=b使用bb=temp修改b,这些都没有排序,所以没有定义行为。虽然结果通常好像表达式是以一种或另一种顺序计算的,但出现此错误的程序可能会以其他方式出现异常。

    您可以在函数调用中使用逗号运算符,但您必须使用括号将其与函数参数区分开来,如printf("%d\n", (temp = a, a = b, b = temp));

    【讨论】:

      【解决方案4】:

      你的表情

      printf("%d\n", temp = a, a = b, b = temp);
      //                     ^      ^ argument separator
      //             ^^^^^^^^  ^^^^^  ^^^^^^^^ 3 arguments
      

      无逗号运算符:要打印 3 个值(2 个被评估但否则会被忽略),1 个转换,Undefined Behaviour(未排序的表达式)。

      我认为你在寻找什么

      printf("%d\n", (temp = a, a = b, b = temp));
      //                      ^      ^ comma operator
      //             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1 argument grouped
      

      逗号运算符,1 个值,1 次转换,假设 temp 为 int 且 temp、b 和 a 是赋值兼容的(它们在您的 sn-p 中)。

      【讨论】:

        【解决方案5】:

        问题在于没有初始化 temp。 表达式“ b = temp ” 将返回错误。 结果为 8

        printf("%d\n", temp = b, b = a)
        

        您可以评估代码并在 printf 中使用单个变量

        编辑 根据this 的回答,你不能说评估是从右到左的,因为它是未定义的。

        【讨论】:

        • 这仍然会导致未定义的行为(b 的写入和读取没有序列点)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-07-05
        • 2018-12-31
        • 2021-11-17
        相关资源
        最近更新 更多