我在测试中看到了这个问题,在测试中我们必须告诉以下代码的输出。
#include<stdio.h>
int main(){
int k = 0;
while(+(+k--)!=0)
k=k++;
printf("%d\n", k);
return 0;
}
输出为-1。我不确定为什么会这样。
该表达式+(+k--)在C中是什么意思?
k=k++是不确定的,但不是不确定的,因为由于混淆的条件而从未执行过它-我投票结束于此问题的重复。
我在测试中看到了这个问题,在测试中我们必须告诉以下代码的输出。
#include<stdio.h>
int main(){
int k = 0;
while(+(+k--)!=0)
k=k++;
printf("%d\n", k);
return 0;
}
输出为-1。我不确定为什么会这样。
该表达式+(+k--)在C中是什么意思?
k=k++是不确定的,但不是不确定的,因为由于混淆的条件而从未执行过它-我投票结束于此问题的重复。
Answers:
[为了记录,自从被接受并投票以来,我已经对该答案进行了相当大的编辑。不过,它仍然说基本相同的内容。]
这段代码深深地,有意地使人困惑。它包含一个可怕的未定义行为的狭窄实例。基本上不可能确定提出这个问题的人是非常,非常聪明还是非常,非常愚蠢。而且此代码可能声称要教给或测验您的“教训”,即一元加号运算符没有做太多事情,当然还不足以应对这种颠覆性的误导。
该代码有两个令人困惑的方面,一个奇怪的条件:
while(+(+k--)!=0)
及其控制的痴呆语句:
k=k++;
我将首先介绍第二部分。
如果您有一个k想要将其递增1 的变量,C不会给您一个,两个,三个,而是四个不同的方法:
k = k + 1k += 1++kk++尽管有这么多赏金(或也许是因为赏金),但一些程序员还是感到困惑和咳嗽,例如
k = k++;
如果您不知道该怎么办,请不要担心:没有人可以。此表达式包含两种不同的尝试来更改k的值(k =部分和k++部分),并且由于C中没有规则说哪个尝试的修改“获胜”,因此像这样的表达式在形式上是不确定的,不仅意味着它没有定义的含义,但是包含它的整个程序都是可疑的。
现在,如果您非常仔细地看,您会发现在此特定程序中,该行k = k++实际上并未执行,因为(正如我们将要看到的)控制条件最初是假的,因此循环运行0次。因此,该特定程序实际上可能未定义-但在病理上仍然令人困惑。
但是您没有询问这k=k++部分。您询问了第一个令人困惑的部分,即+(+k--)!=0条件。这看起来很奇怪,因为这是奇怪的。没有人会在真正的程序中编写过这样的代码。因此,没有理由学习如何理解它。(是的,确实如此,探索系统的边界可以帮助您了解系统的优缺点,但是我的书在富有想象力的,发人深省的探索与笨拙的,有辱骂性的探索之间有一个非常清晰的界线,这种表达方式在该行的错误一侧。)
无论如何,让我们检查一下+(+k--)!=0。(然后,让我们忘记所有这些。)必须从内而外地理解这样的表达式。我想你知道
k--
做。它采用k的当前值并将其“返回”到表达式的其余部分,并且它或多或少同时减少k,即,将数量存储k-1回k。
但是那怎么+办?这是一元加号,而不是二进制加号。就像一元减。您知道二进制减号会减去:
a - b
从a减去b。而且您知道一元减法会否定事物:
-a
给你a的负数。一进制+所做的是...基本上什么也没有。 在将正值更改为正值并将负值更改为负值后,可以+a提供您a的价值。所以表达
+k--
给你的东西k--给了你,那就是它k的旧价值。
但是我们还没有完成,因为我们有
+(+k--)
这仅需要+k--给您的一切,然后+再对它应用一元。所以它给了你什么,+k--给了你什么,什么k--给了你,那是k旧的价值。
所以最后,条件
while(+(+k--)!=0)
与更普通的情况完全一样
while(k-- != 0)
会做。(它的作用与看起来更复杂的条件相同while(+(+(+(+k--)))!=0)。并且括号实际上不是必需的;它也与应做的事情相同while(+ + + +k--!=0)。)
甚至弄清楚什么是“正常”情况
while(k-- != 0)
确实有点棘手。此循环中发生了两件事:由于该循环可能多次运行,因此我们将:
k--,使自己k越来越小,而且但是k--,在决定是否再进行一次循环之前(或正在进行),我们会立即进行操作。并记住,在递减之前,k--“返回”的旧值k。在此程序中,初始值为k0。因此k--将“返回”旧值0,然后更新k为-1。但是剩下的条件是!= 0-但是,正如我们刚刚看到的,第一次测试条件时,我们得到了0。因此,我们不会在循环中进行任何行程,因此,我们不会尝试执行有问题的陈述k=k++。
换句话说,在这个特定的循环中,尽管我说“正在发生两件事”,但事实证明,事情1发生了一次,而事情2发生了零次。
无论如何,我希望现在已经足够清楚了,为什么这个糟糕的借口导致程序最终将-1打印为k。通常,我不喜欢回答这样的测验问题-感觉像在作弊-但是在这种情况下,由于我非常反对练习的全部内容,因此我不介意。
乍看起来,这段代码会调用未定义的行为,但事实并非如此。
首先,让我们正确设置代码格式:
#include<stdio.h>
int main(){
int k = 0;
while(+(+k--)!=0)
k=k++;
printf("%d\n", k);
return 0;
}
现在,我们可以看到该语句k=k++;位于循环内部。
现在让我们跟踪程序:
首次评估循环条件时,k其值为0。该表达式k--的当前值为k,该值为0,并且k由于副作用而递减。因此,在此语句之后,值k是-1。
该+表达式的前导对值没有影响,因此将其+k--评估为0并类似地+(+k--)评估为0。
然后!=评估操作员。由于0!=0为false,因此不会输入循环的主体。如果输入了正文,则将调用未定义的行为,因为k=k++读取和写入都k没有序列点。但是没有进入循环,所以没有UB。
最后,k打印的值为-1。
if (x != NULL) *x = 42;这是何时未定义x == NULL?当然不是。未执行的代码部分不会发生未定义的行为。行为这个词是一个提示。未执行的代码没有行为,未定义或其他行为。
k=k++在质量上与有所不同*x=42。如果x是有效的指针,则后者是明确定义的,但是无论如何,前者都是未定义的。(我承认您可能是对的,但是我还是不会再对此争论,并且我越来越担心我们已经被精通了。)