我=(i,++ i,1)+1; 做?
在阅读了关于未定义的行为和顺序点的答案后,我写了一个小程序:
#include <stdio.h> int main(void) { int i = 5; i = (i, ++i, 1) + 1; printf("%d\n", i); return 0; }
输出是2
。 哦,上帝,我没有看到递减! 这里发生了什么?
另外,在编译上面的代码的时候,我得到了一个警告:
px.c:5:8:警告:逗号expression式的左侧操作数不起作用
[-Wunused-value] i = (i, ++i, 1) + 1; ^
为什么? 但可能会自动回答我的第一个问题的答案。
在expression式(i, ++i, 1)
,逗号使用的是逗号运算符
逗号运算符(由令牌表示)是一个二元运算符,它评估其第一个操作数并放弃结果,然后评估第二个操作数并返回该值(和types)。
因为它放弃了它的第一个操作数,所以通常只在第一个操作数具有理想的副作用时才有用 。 如果第一个操作数的副作用没有发生,那么编译器可能会产生一个关于expression式的警告而不起作用。
所以,在上面的expression式中,最左边的i
将被评估,其值将被丢弃。 然后++i
将被评估,并将i
1递增,再次expression式++i
的价值将被丢弃, 但对i
的副作用是永久性的 。 然后1
将被评估,expression式的值将是1
。
这相当于
i; // Evaluate i and discard its value. This has no effect. ++i; // Evaluate i and increment it by 1 and discard the value of expression ++i i = 1 + 1;
请注意, 上述expression式是完全有效的,并且不会调用未定义的行为,因为在逗号运算符的左侧和右侧操作数的评估之间存在序列点 。
逗号运算符从C11
第6.5.17
章引用
逗号运算符的左操作数被评估为voidexpression式; 它的评估与右操作数的评估之间有一个顺序点。 然后评估右操作数; 结果有它的types和价值。
所以,在你的情况下,
(i, ++i, 1)
被评估为
-
i
,被评估为一个无效的expression,价值被丢弃 -
++i
被评估为一个voidexpression式,值被丢弃 - 最后,
1
,价值返回。
所以,最后的声明看起来像
i = 1 + 1;
i
得到2
。 我想这回答你的两个问题,
- 我如何获得价值2?
- 为什么有一个警告信息?
注意:由于在左手操作数的评估之后出现了一个序列点 ,所以像(i, ++i, 1)
这样的expression式不会调用UB,正如人们通常认为的那样。
i = (i, ++i, 1) + 1;
我们一步一步分析一下。
(i, // is evaluated but ignored, there are other expressions after comma ++i, // i is updated but the resulting value is ignored too 1) // this value is finally used + 1 // 1 is added to the previous value 1
所以我们得到2.现在最后的任务是:
i = 2;
无论在我被覆盖之前,现在。
结果
(i, ++i, 1)
是
1
对于
(i,++i,1)
评估发生时,
操作员丢弃评估值并将保留最右边的值1
所以
i = 1 + 1 = 2
您会在逗号运算符的wiki页面上find一些很好的阅读。
基本上,它
…评估其第一个操作数并放弃结果,然后评估第二个操作数并返回该值(和types)。
这意味着
(i, i++, 1)
依次评估i
,放弃结果,评估i++
,放弃结果,然后评估并返回1
。
你需要知道逗号操作符在这里做什么:
你的表情:
(i, ++i, 1)
评估第一个expression式i
,评估第二个expression式++i
,并且为整个expression式返回第三个expression式1
。
所以结果是: i = 1 + 1
。
正如你所看到的,对于你的奖金问题,第一个expression式i
根本没有任何作用,所以编译器会抱怨。
逗号有一个“反”优先级。 这是您将从旧书和IBM的手册(70年代/ 80年代)中获得的。 所以最后的'命令'是在父expression式中使用的。
在现代C中,它的使用是奇怪的,但在旧的C(ANSI)中是非常有趣的:
do { /* bla bla bla, consider conditional flow with several continue's */ } while ( prepAnything(), doSomethingElse(), logic_operation);
虽然所有操作(函数)都是从左到右调用的,但只有最后一个expression式才会被用作条件“while”的结果。 这样可以防止goto在条件检查之前保持一个唯一的命令块运行。
编辑:这也避免了一个处理函数的调用,它可以处理左操作数的所有逻辑,并返回逻辑结果。 请记住,我们在C的过去没有内联函数。因此,这可以避免调用开销。