百科问答小站 logo
百科问答小站 font logo



为什么C语言中计算机认为0是假的,其他数都是真的? 第1页

  

user avatar   gao-tian-50 网友的相关建议: 
      

这么多答jnz的小伙伴,jnz是x86的ISA,x86是1978年Intel搞出来的东西,C是1969到1973年在贝尔实验室被发明的,这不是「你爷爷姓李因为你爸爸姓李么?」

因此我们要去研究为啥C语言中认为0是假,其他都是真,就得回到上个世纪六七十年代,C被发明的时候。

当年贝尔实验室去设计C的主要目的,是用一个高级语言重写Unix。而C这个名字是从哪里来的呢?是因为他有个前身叫做B(现在已经死翘翘了)……B同样也是贝尔实验室发明C的大神发明的,也是在Unix上写东西的。而通过wiki(B (programming language))我们可以看到,在B语言里,就已经用了「0为假,其他为真」这个概念了。而这个概念继承给了C。

当然,B语言也有爸爸,B的爸爸叫做BCPL(现在也已经死翘翘了)。有趣的是,BCPL并没有用这个「0为假,其他为真」的概念!在BCPL里,假是全0,真是全1,从而得到了一个很有趣的结果就是「~False == True」。把假按位取反就是真,这看起来是个不错的性质。那如果if后面跟了一个不真不假的数呢?根据implementation来决定(勘误,这里用ub不是非常合适)。

追祖溯源到这里,我们应该已经基本确定,C中的这个约定,是来自于B语言。那我们就看看B语言发明时候的环境。B语言的发明是和Unix紧密相关的,而Unix当时运行在一个叫做PDP-7的电脑(勘误,这里随手就打上芯片了,PDP-7是个电脑)上(后来出了PDP-11,B语言遇到了瓶颈,才出现了C)。而这款芯片也是有自己的ISA的,其中有个很重要的命令叫作isz(index and skip if zero),这个命令被大量地用作branch。

我们知道,早期的程序语言,是比较讲究高级语言和assembly之间的对应的(这样compiler好写,也容易优化)。也正是早期assembly这种和0比较的语法存在,导致的B语言中出现了和0比较的这种现象,然后导致的C继承了(或者说也使用了)这种现象。

在PDP-11中,就出现了我们很熟悉的bne beq之类的assembly了,同样也是和0比较得出结果然后branch的。jnz那是十年之后的事情了~

你以为这就结束了么?当然没有~为什么无论是早期的assembly,还是近现代的assembly,都会有大量的和0比较的语法存在?和0比较有什么得天独厚的优势么?为什么不是1代表True,剩下都是False?为什么不是0到9代表False,剩下都是True?

这要从编码讲起。我们知道在计算机中,整数是以补码的形式被编码的。我们认为的数字0,就是每个bit都是0,而正整数是其二进制的表示形式,负整数的最高位是1,用补码表示。

让我们看回电路,我们知道任何「程序」的实现,最终都是要电路来完成的。我们有一个或者16位或者32位或者64位的「信号」(代表着我们想要比较的数),判断它是不是全是0,是要比判断它是不是一个特定的数(比如0xa5a5a5a5)要容易的!我们可以在输入端不用任何反向器的情况下去进行判断(比较极端的比如N-input NAND门)。而判断是否为一个特殊的数,是一定要多少对输入进行处理的(比较器或者反向器)。也就是说,在电路的角度看,判断一个N位二进制数是否是0,是比判断它是否是一个特殊的数,实现要更简单更快的(理论上判断是否都是1速度也很快)。

不仅如此,二进制补码带给我们的另外一个效应是,不光判断是否是0速度快,判断是否大于或者小于0,速度优势更明显!只需要判断一下符号位,然后再看除了符号位之外的那些是不是0就可以了。如果是大于等于或者小于等于,那就仅仅需要判断符号位!而做过一点逻辑电路的都知道,你想在RTL级去判断一个信号是否大于一个特殊的数,基本会被累死。那是一个非常麻烦的事情。

因此,从硬件的角度来看,和0去做比较,有着明显的优势。而在逻辑电路里0一般代表False,所以软件就自然地把数分为了0和非0的,也就衍生出了False和True。

总结一下,是硬件的特性,导致了判断一个多路信号和全0的关系更为简单和快速,又因为我们采用的编码,导致了assembly level上去比较一个寄存器和0的关系更加便捷,最后导致了高级语言C里的if语句,把0当作False,非0当作True。

当然,随着软件硬件越来越细分的功能,很多人开始对C的这个设计有所质疑。我个人是比较赞同C的这个设计在大规模high-level的工程中是弊大于利的。我觉得对于一个当代的高层语言,拥有一个完善的boolean变量是非常有价值的,可以减少很多无谓的错误。

但是我依然很喜欢C里的各种「设计缺陷」,这些「设计缺陷」其实带来了很多语法糖和有趣的性质。

       if (ptr) {     // I'm not a NULL pointer! }  if (func1() | func2() | func3()) {     // What's the difference? }     

user avatar   theninth 网友的相关建议: 
      

我想搞个笑

因为二进制里面除了0,其他的数都有1


user avatar   pansz 网友的相关建议: 
      

C 语言是一种相对比较底层的语言,这个语言可以认为是试图用高级语言对汇编的元素进行复刻而生。

对于判断 0 或者非 0 两种状态,汇编有直接对应的指令(jnz跟jz指令),如果要判断是否等于 3 ,则需要额外的一条指令将目标减 3 然后再同 0 进行比较,这样指令数量增加了,也就耗费了额外的时钟周期。所以,C 作为一种相对贴近汇编的底层编程语言,if 命令只需要判断 0 跟非零就够了,它翻译成汇编最简洁。

早年间的始祖级程序员曾经教导弟子:条件判断尽量只跟 0 进行比较,效率最高。其原理正是因为早年间汇编的特性导致。

现在或者将来,或许会有其它的汇编架构,允许单指令直接与非零变量进行比较并跳转,但在 C 语言设计的当初,汇编必定是与 0 比较效率最高的。在那个程序员大都懂一点汇编的年代,做出 C 语言只与零比较这个决定,简直是天经地义的。


当然,C 语言中的 0 并非一直用来表示负面概念。对于 if 语句的判断来说, 0 为假,非零为真。但对于 C 语言函数的返回值来说,通常是返回 0 代表成功,返回非零代表错误代码。

所以,C 语言确实是只需要 0 跟非零两种状态判断,但并非永远用 0 代表负面的那个方向。


user avatar   miloyip 网友的相关建议: 
      

第一个不用看答案光看问题就被震惊了的问题。




  

相关话题

  低耦合或代码重复在该情况中该如何抉择? 
  Linux 图形界面的显示原理是什么? 
  国内Java面试总是问StringBuffer,StringBuilder区别是啥?档次为什么这么低? 
  为什么C语言中计算机认为0是假的,其他数都是真的? 
  学习 C/C++ ,有什么书籍推荐? 
  写C with class很丢人么? 
  一个程序员多年累计编写一百万行代码是什么体验? 
  在函数的入口处对参数的合法性进行检查是一个值得提倡的好习惯吗? 
  GitHub 上可供新手阅读和玩耍的 Java 项目有哪些? 
  国外的程序员是如何保养「革命的本钱」? 

前一个讨论
是什么环境使诸如咪蒙之类的公众号繁殖?
下一个讨论
旅行者一号发射时 CCD 还没有问世,那它是靠什么照相并且回传回地球的?





© 2024-11-22 - tinynew.org. All Rights Reserved.
© 2024-11-22 - tinynew.org. 保留所有权利