为什么左侧的“取消引用”和“地址”运营商?
在C(以及一些其他类C语言)中,我们有2个一元运算符用于处理指针:解引用运算符( *
)和’运算符地址’( &
)。 它们是一元运算符,它引入了操作顺序的不确定性,例如:
*ptr->field
要么
*arr[id]
操作的顺序由标准严格定义,但从人的角度来看,这是令人困惑的。 如果*
运算符是一个正确的一元运算符,则顺序很明显,不需要额外的括号:
ptr*->field vs ptr->field*
和
arr*[id] vs arr[id]*
因此,运营商为什么会离开一元而不是正确,这是有充分理由的。 我想到的一件事是类型的声明。 左操作符保持在类型名称附近( char *a
vs char a*
),但是有类型声明,已经破坏了这个规则,所以为什么要打扰( char a[num]
, char (*a)(char)
等) 。
显然,这种方法也有一些问题,比如
val*=2
对于val = val * 2
或dereference并指定val* = 2
这将是*=
短手。 然而,这可以通过在解除引用的情况下在*
和=
令牌之间需要空白来容易地解决。 再一次,没有任何开创性的,因为有这样一个规则的先例( - -a
vs --a
)。
那么为什么他们离开而不是正确的运营商呢?
编辑:我想指出,我问了这个问题,因为C的许多更奇怪的方面都有有趣的解释,为什么它们是这样的,就像->
运算符或类型声明或索引的存在一样从0开始。依此类推。 原因可能不再有效,但我认为它们仍然很有趣。
确实有一个权威的来源: 语言的创造者Dennis M. Ritchie的“C语言的发展” :
语法意外导致了语言的复杂性。 在C语言中拼写为
*
的间接运算符在语法上是一元前缀运算符, 就像在BCPL和B中一样 。 这在简单表达式中很有效,但在更复杂的情况下,需要使用括号来指导解析。 例如,为了区分间接通过函数返回的值与调用指针指定的函数,分别写入*fp()
和(*pf)()
。 表达式中使用的样式贯穿声明,因此可以声明名称int *fp(); int (*pf)();
在更华丽但仍然现实的情况下,事情变得更糟:
int *(*pfp)();
是一个指向函数的指针,该函数返回一个指向整数的指针。 发生了两种效应。 最重要的是,C有一套相对丰富的描述类型的方法(比如用Pascal比较)。 例如,C-Algol 68表达的语言声明 – 描述同样难以理解的对象,仅仅因为对象本身很复杂。 第二个效果归功于语法的细节。 C中的声明必须以“由内而外”的方式阅读,许多人都难以理解[ Anderson 80 ]。 Sethi [ Sethi 81 ]观察到,如果将间接运算符作为后缀运算符而不是前缀,许多嵌套的声明和表达式会变得更简单,但到那时为时已经太晚了。
因此*
在C的左边是因为它在B的左边 。
B部分基于BCPL ,其中解除引用运算符!
。 这是在左边; 二进制!
是一个数组索引操作符:
a!b
相当于
!(a+b)
。
!a
是地址由a给出的小区的内容; 它可以出现在作业的左侧。
然而,50年历史的BCPL手册甚至没有提及!
运算符 – 相反,运算符是单词:unary lv
和rv
。 由于这些被理解为它们是函数,因此它们在操作数之前是自然的; 之后,可以用语法糖取代较长的rv a
!a
许多当前的C操作员实践可以通过这条路线进行追踪。 B类似于a[b]
相当于*(a + b)
到*(b + a)
到b[a]
,就像在BCPL中一样,可以使用a!b
<=> b!a
。
请注意,在B变量是无类型的 ,所以肯定与声明的相似性不能成为在左边使用*
的原因。
因此,在C中左边是一元*
的原因是无聊的“在一个简单的程序中没有任何问题,一元*
在左边,在每个人习惯于在其他人中使用解除引用操作符的位置语言,没有人真正认为其他方式会更好,直到改变它为时已晚“ 。