这段代码非常令人困惑,也许是故意的。它包含了一个勉强避免的恐惧实例未定义的行为。很难知道提出这个问题的人是非常非常聪明还是非常非常愚蠢。这段代码可能声称要教给你或测验你的“教训”——即一元加运算符没有做太多事情——我认为,这个教训还不够重要,值得这种颠覆性的误导。
代码有两个令人困惑的方面,即奇怪的情况:
while(+(+k--)!=0)
以及它控制的疯狂语句:
k=k++;
我将首先介绍第二部分。
如果你有一个像这样的变量k
如果你想加 1,C 给你的不是一种、不是两种、不是三种,而是四种不同的方法:
k = k + 1
k += 1
++k
k++
尽管有这样的赏金(或者可能正因为如此),一些程序员还是感到困惑并吐出像这样的扭曲:
k = k++;
如果您无法弄清楚它应该做什么,请不要担心:没有人能做到。该表达式包含两种不同的改变尝试k
的值(k =
部分,以及k++
部分),并且因为 C 中没有规则来说明哪个尝试的修改“获胜”,所以这样的表达式在形式上是不明确的,这不仅意味着它有no定义了含义,但包含它的整个程序是可疑的。
现在,如果你看very仔细观察,你会发现在这个特定的程序中,这一行k = k++
实际上并没有被执行,因为(正如我们即将看到的)控制条件最初为 false,因此循环运行了 0 次。所以这个特定的程序可能不会actually是未定义的——但它仍然在病态上令人困惑。
也可以看看这些规范的答案关于此类未定义行为的所有问题。
但你没有问k=k++
部分。你问了第一个令人困惑的部分,+(+k--)!=0
健康)状况。这看起来很奇怪,因为它is奇怪的。没有人会在真实的程序中编写这样的代码。所以没有太多理由去学习如何理解它。 (是的,这是真的,探索系统的边界可以帮助你了解它的优点,但在我的书中,富有想象力、发人深省的探索与愚蠢、滥用的探索之间有一条界限,这种表达方式显然是错误的该线的一侧。)
无论如何,让我们检查一下+(+k--)!=0
。 (这样做之后,让我们忘记这一切。)任何像这样的表达都必须从内到外地理解。我想你知道什么
k--
做。它需要k
的当前值并将其“返回”到表达式的其余部分,并且它或多或少同时递减k
,即存储数量k-1
回到k
.
但那又是什么呢?+
做?这是unary加,不是二进制加。就像一元减号一样。您知道二进制减法执行减法:表达式
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
,然后递减它。在这个程序中,初始值为k
is 0. So k--
将“返回”旧值 0,然后更新k
至-1。但剩下的条件是!= 0
——事实并非如此0 != 0
。也就是说,0is等于 0,因此我们不会执行任何循环,因此我们不会尝试执行有问题的语句k=k++
at all.
换句话说,在这个特定的循环中,虽然我说“有两件事正在发生”,但事实证明,事情 1 发生了一次,但事情 2 发生了零次。
无论如何,我希望现在已经足够清楚为什么程序的这个糟糕借口最终会打印 -1 作为最终值k
。通常,我不喜欢回答这样的测验问题——这感觉就像作弊——但在这种情况下,因为我根本不同意整个练习的要点,所以我不介意。