为什么在C样式语言中逻辑NOT运算符是“!”而不是“ ~~”?


39

对于二进制运算符,我们同时具有按位和逻辑运算符:

& bitwise AND
| bitwise OR

&& logical AND
|| logical OR

但是,NOT(一元运算符)的行为有所不同。有〜用于按位和!为逻辑。

我认识到与AND和OR相对,NOT是一元运算,但是我无法想到设计师为何选择偏离单数位和双数逻辑的原则,而是改用其他字符的原因。我猜您可能读错了它,就像总是返回操作数值的双按位运算一样。但这对我来说似乎不是一个真正的问题。

我有什么原因想念吗?


7
因为如果 !!表示不合逻辑,我如何将42变成1?:)
candied_orange

9
~~如果您遵循逻辑运算符是按位运算符加倍的模式,那么对于逻辑NOT而言,会不会更加一致?
Bart van Ingen Schenau

9
首先,如果是为了保持一致性,那将是〜和~~将and和or加倍与短路相关联;而且逻辑上没有短路。
Christophe

3
我怀疑在典型的用例中,潜在的设计原因是视觉清晰度和区别。二进制(即两个操作数)运算符是不固定的(并且往往用空格分隔),而一元运算符是前缀的(并且通常不分隔)。
史蒂夫

7
正如一些评论已经提到(并为那些不愿追随谁这个环节!!foo是一个不常见(不不常见)成语它归零或-非零参数来?0或者1
基思·汤普森

Answers:


108

奇怪的是,C风格编程语言的历史并非始于C。

Dennis Ritchie在本文中很好地解释了C诞生的挑战。

当阅读它时,很明显C继承了其语言设计的一部分,源于其前身BCPL,尤其是运算符。上述文章的“新生儿C”部分说明了BCPL &和BCPL如何|被两个新的运算符&&和丰富||。原因是:

  • 由于将其与 ==
  • 不同的评估逻辑:左到右的评价与短路 (即,当afalsea&&bb不评估)。

有趣的是,这种加倍操作不会对读者造成任何歧义:a && b不会被误解为a(&(&b))。从解析的角度来看,也没有歧义:&b如果b是左值,这是有意义的,但是它将是一个指针,而按位运算则&需要一个整数操作数,因此逻辑与将是唯一合理的选择。

BCPL已用于~按位求反。因此,从一致性的角度来看,可以将它加倍以赋予~~其逻辑上的意义。不幸的是,这是非常模糊的,因为~是一元运算符:~~b也可能意味着~(~b))。这就是为什么必须为缺失的否定选择另一个符号的原因。


10
解析器无法消除这两种情况的歧义,因此语言设计者必须这样做。
BobDalgleish

16
@Steve:确实,在C和类似C的语言中已经存在许多类似的问题。当解析器看到(t)+1(t)and 的加法符1+1类型的强制转换t吗?C ++设计必须解决如何>>正确包含模板的问题。等等。
埃里克·利珀特

6
@ user2357112我认为重点在于,可以使标记化程序盲目地将其&&作为单个&&标记而不是两个&标记,因为a & (&b)解释不是要写的合理的东西,因此人类永远都不会那样做,并且对此感到惊讶编译器将其视为a && b。两者!(!a)!!a对于人类来说都是可能的意思,因此对于编译器来说,使用任意的令牌化级别规则来解决歧义是一个坏主意。

17
!!不仅可能/合理地编写,而且规范的“转换为布尔”成语。
R ..

4
我认为dan04指的是--avs 的歧义-(-a),两者在语法上均有效,但具有不同的语义。
Ruslan

49

我想不出为什么设计师选择偏离单数位和双数逻辑的原则的原因,

首先,这不是原则。一旦意识到这一点,那就更有意义了。

考虑&vs 的更好方法&&不是二进制布尔值。更好的方法是将它们视为渴望懒惰。该&运营商执行左侧和右侧,然后计算结果。该&&运营商执行的左侧,然后执行右侧只在必要时计算出结果。

而且,不要考虑“二进制”和“布尔”,而要考虑实际发生的事情。“二进制”版本仅对已打包成单词的布尔数组执行布尔运算

因此,让我们放在一起。对布尔数组执行惰性操作有意义吗?不可以,因为没有“左侧”需要首先检查。首先检查32个“左侧”。因此,我们将惰性操作限制为单个布尔值,这就是您的直觉,其中一个是“二进制”,一个是“布尔”,但这是设计的结果,而不是设计本身!

当您以这种方式思考时,很清楚为什么没有!!和没有^^。这两个运算符都不具有可以跳过对其中一个操作数进行分析的属性。没有“懒惰” notxor

其他语言使这一点更加清楚。例如,某些语言用and“渴望和” and also来表示,而用“懒惰和”来表示。其他语言也更清楚地指出&&&它们不是“二进制”和“布尔”。例如,在C#中,两个版本都可以将布尔值作为操作数。


2
谢谢。这是我真正的大开眼界。太糟糕了,我不能接受两个答案。
Martin Maat

10
我不认为这是思考的好方法&&&。急切是&和之间的区别之一&&,,&其行为与急切版本完全不同&&,特别是在&&支持专用布尔类型以外的类型的语言中。
user2357112

14
例如,在C和C ++中,1 & 2结果与完全不同1 && 2
user2357112

7
@ZizyArcher:就像我在上面的评论中指出的那样,省略boolC类型的决定具有连锁反应。我们两者都需要!~因为一个的意思是“将一个整数作为一个布尔值处理”,一个的意思是“将一个整数作为布尔值的打包数组进行处理”。如果您拥有不同的布尔类型和整数类型,那么您只能拥有一个运算符,我认为这是更好的设计,但我们在该运算符上迟了近50年。C#保留了此设计以便于熟悉。
埃里克·利珀特

3
@Steve:如果答案似乎很荒谬,那么我在某处提出了一个表达不充分的论点,我们不应该依赖权威的论点。您能说些荒谬的事吗?
埃里克·利珀特

21

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 定义TRUEas 1FALSEas 0意味着该身份true = ~ false不再成立,并且它放弃了强类型,这种强类型使Algol可以消除按位和逻辑表达式之间的歧义。这就需要一个新的逻辑非运算符,设计人员选择!,可能是因为已经不等于了!=,它看起来像是通过等号的竖线。他们没有遵循相同的约定,&&或者||因为两者都不存在。

可以说,它们应该具有:&B中的运算符按设计中断。在B和C中,1 & 2 == FALSE尽管12都是真值,并且没有直观的方式来表达B中的逻辑运算。这是C试图通过添加&&和来部分纠正的一个错误||,但当时的主要关注是最终使短路工作,并使程序运行更快。这样的证明是^^1 ^ 2即使两个操作数都是真实的,也不存在一个真实的值,但是它不能从短路中受益。


4
+1。我认为这是围绕这些运算符的演进的非常不错的导览。
史蒂夫

BTW,符号/幅值和补码机也需要单独的按位与逻辑取反,即使输入已经被布尔化了。 ~0(所有位均置1)是一个人的补数负零(或陷阱表示)。符号/幅值~0是最大数的负数。
彼得·科德斯

@PeterCordes你是完全正确的。我只是专注于两个补码机,因为它们更重要。也许值得一注。
戴维斯洛

我认为我的评论就足够了,但是是的,也许带括号(对1的补码或符号/幅度均无效)将是一个很好的编辑。
彼得·科德斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.