TL; DR
C 从另一种语言继承了!
and ~
运算符。双方&&
并||
分别由不同的人十年后添加。
长答案
从历史上看,C是从基于BCPL的早期语言B发展而来的,而BCPL是基于Algol的CPL。
Algol是C ++,Java和C#的曾祖父,它以对程序员来说很直观的方式定义了true和false:“真实值,被视为二进制数(true对应于1,false对应于0),等于内在积分值”。但是,这样做的一个缺点是逻辑和按位运算不能是同一操作:在任何现代计算机上,~0
等于-1而不是1 ~1
等于-2而不是0。(即使在已有60年历史的大型机上,它也~0
表示- 0或INT_MIN
,~0 != 1
在每个制造过的CPU上,并且C语言标准已经对其要求了很多年,而它的大多数子语言甚至根本不需要支持符号和大小或补码。)
Algol通过采用不同的模式并在布尔和整数模式下对运算符进行不同的解释来解决此问题。也就是说,按位运算是对整数类型的一种,而逻辑运算是对布尔类型的一种。
BCPL有一个单独的布尔类型,但只有一个not
运算符,对于按位和逻辑而言都不如此。这个C的早期先驱完成这项工作的方式是:
Rvalue为true是完全由1组成的位模式。false的Rvalue为零。
注意 true = ~ false
(您会注意到,术语rvalue在C系列语言中已经演变为完全不同的含义。今天,我们将其称为C中的“对象表示”。)
此定义将允许逻辑和按位不使用同一机器语言指令。如果C走了那条路,那么全世界的头文件都会说#define TRUE -1
。
但是B编程语言是弱类型的,并且没有布尔甚至浮点类型。一切都int
与其后继程序C 等效。这对于使该语言定义一个程序使用真或假以外的值作为逻辑值时所发生的事情是一个好主意。它首先将一个真实的表达式定义为“不等于零”。这在运行它的微型计算机上非常有效,该微型计算机具有CPU零标志。
当时有一种选择:相同的CPU也带有负标志,并且BCPL的真值是-1,因此B可能会将所有负数都定义为真,而将所有非负数都定义为虚假。(这种方法有一个残余:在UNIX上,由同一个人同时开发的许多系统调用将所有错误代码都定义为负整数。失败时,其许多系统调用会返回几个不同的负值之一。)值得庆幸的是:情况可能会更糟!
但是用B 定义TRUE
as 1
和FALSE
as 0
意味着该身份true = ~ false
不再成立,并且它放弃了强类型,这种强类型使Algol可以消除按位和逻辑表达式之间的歧义。这就需要一个新的逻辑非运算符,设计人员选择!
,可能是因为已经不等于了!=
,它看起来像是通过等号的竖线。他们没有遵循相同的约定,&&
或者||
因为两者都不存在。
可以说,它们应该具有:&
B中的运算符按设计中断。在B和C中,1 & 2 == FALSE
尽管1
和2
都是真值,并且没有直观的方式来表达B中的逻辑运算。这是C试图通过添加&&
和来部分纠正的一个错误||
,但当时的主要关注是最终使短路工作,并使程序运行更快。这样的证明是^^
:1 ^ 2
即使两个操作数都是真实的,也不存在一个真实的值,但是它不能从短路中受益。