为什么“取消引用”和“地址”运算符位于左侧?

2024-02-01

在 C(以及其他一些类似 C 的语言)中,我们有 2 个一元运算符用于处理指针:解引用运算符 (*) 和“地址”运算符 (&)。他们是left一元运算符,这会带来运算顺序的不确定性,例如:

*ptr->field

or

*arr[id]

操作顺序is标准严格定义,但从人的角度来看,却令人困惑。如果*操作员是一个right一元运算符,顺序很明显并且不需要额外的括号:

ptr*->field vs ptr->field*

and

arr*[id] vs arr[id]*

那么运营商有充分的理由吗?left一元,而不是右。我想到的一件事是类型的声明。左运算符位于类型名称附近 (char *a vs char a*),但是有些类型声明已经违反了这条规则,所以为什么还要麻烦(char a[num], char (*a)(char), etc).

显然,这种方法也存在一些问题,例如

 val*=2

这将是一个*=简写为val = val * 2或取消引用并分配val* = 2。 然而,这可以通过要求之间有一个空格来轻松解决* and =取消引用时的令牌。再说一次,没有什么开创性的,因为有这样的规则的先例(- -a vs --a).

那么为什么他们是左操作员而不是右操作员呢?

编辑: 我想指出,我问这个问题是因为 C 的许多奇怪的方面都有有趣的解释,为什么它们是这样的,比如的存在->操作员 https://stackoverflow.com/questions/13366083/why-does-the-arrow-operator-in-c-exist或类型声明或从 0 开始的索引。等等。这些理由可能不再有效,但在我看来它们仍然很有趣。


确实有is权威来源:《C 语言的发展》,作者:C 语言创始人 Dennis M. Ritchie https://www.bell-labs.com/usr/dmr/www/chist.html:

语法上的意外导致了语言的复杂性。间接运算符,拼写为*在 C 中,在语法上是一元前缀运算符,就像 BCPL 和 B 中一样。这在简单表达式中效果很好,但在更复杂的情况下,需要括号来指导解析。例如,为了通过函数返回的值来区分间接调用和调用由指针指定的函数,可以这样写:*fp() and (*pf)()分别。表达式中使用的样式会延续到声明中,因此可以声明名称

int *fp();
int (*pf)();

在更华丽但仍然现实的情况下,事情会变得更糟:

int *(*pfp)();

是一个指向返回整数指针的函数的指针。有两种效应发生。最重要的是,C 拥有一套相对丰富的描述类型的方法(例如与 Pascal 相比)。像 C 这样富有表现力的语言(例如 Algol 68)中的声明描述的对象同样难以理解,仅仅是因为对象本身很复杂。第二个效果归因于语法细节。 C 中的声明必须以“由内而外”的方式来阅读,许多人发现这很难理解 [安德森 80 https://dl.acm.org/citation.cfm?id=947627]. Sethi [Sethi 81 http://onlinelibrary.wiley.com/doi/10.1002/spe.4380110606/full] 观察到,如果间接运算符被视为后缀运算符而不是前缀,许多嵌套声明和表达式会变得更简单,但是到那时改变已经太晚了。


因此原因是*位于左侧C是因为它是在左边B https://www.thinkage.ca/english/gcos/expl/b/manu/manu.html#Section6_3.

B部分基于BCPL https://www.lysator.liu.se/c/clive-on-bcpl.html,其中解引用运算符是!。 这是在左边;二进制! was an 数组索引操作员:

a!b

相当于!(a+b).

!a

是地址由 a 给出的单元格的内容;它可以出现在作业的左侧。

然而50岁的人BCPL https://www.bell-labs.com/usr/dmr/www/bcpl.html手册甚至没有提及!运算符 - 相反,运算符是单词:一元lv and rv。由于它们被理解为函数,因此它们很自然地位于操作数之前;后来又长了rv a然后可以用语法糖代替!a.


当前的许多 C 操作符实践都可以通过这条路线进行追踪。 B同样有a[b]相当于*(a + b) to *(b + a) to b[a]就像在 BCPL 中一样可以使用a!bb!a.

请注意,B 中的变量是untyped,所以与声明的相似性当然不可能成为使用的原因*在左边。

所以一元的原因*在 C 语言中位于左边和“使用一元的更简单的程序没有任何问题*在左边,每个人都习惯在其他语言中使用解引用运算符,没有人真正认为其他方式会更好,直到为时已晚,无法更改它”.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么“取消引用”和“地址”运算符位于左侧? 的相关文章

  • 前向声明类型和“已声明为类类型的非类类型”

    我对以下代码有问题 template
  • 现代 C++ 编译器是否能够在某些情况下避免调用 const 函数两次?

    例如 如果我有以下代码 class SomeDataProcessor public bool calc const SomeData d1 const SomeData d2 const private Some non mutable
  • 循环遍历 C 结构中的元素以提取单个元素的值和数据类型

    我有一个要求 我有一个 C 语言的大结构 由大约 30 多个不同数据类型的不同元素组成 typedef struct type1 element1 type2 element2 type3 element3 type2 element4 1
  • 当事件button.click发生时,如何获取按钮名称/标签?

    我以编程方式制作按钮并将它们添加到堆栈面板中 以便每次用户导航到页面时按钮都会发生变化 我正在尝试做这样的事情 当我单击创建的按钮时 它将获取按钮的标签并转到正确的页面 但是 我无法使用 RoutedEventHandler 访问按钮元素
  • 如何将 SOLID 原则应用到现有项目中

    我对这个问题的主观性表示歉意 但我有点卡住了 我希望之前处理过这个问题的人能够提供一些指导和建议 我有 现在已经成为 一个用 C 2 0 编写的非常大的 RESTful API 项目 并且我的一些类已经变得巨大 我的主要 API 类就是一个
  • java中如何重新初始化int数组

    class PassingRefByVal static void Change int pArray pArray 0 888 This change affects the original element pArray new int
  • RestSharp获取序列化输出

    我正在寻找一种方法来访问 AddBody 调用的序列化结果 我正在使用内置的 RestSharp 序列化器 例子 class Foo public string FooField void SendRecord var f new Foo
  • 如何在 C# Designer.cs 代码中使用常量字符串?

    如何在 designer cs 文件中引用常量字符串 一个直接的答案是在我的 cs 文件中创建一个私有字符串变量 然后编辑 Designer cs 文件以使用此变量 而不是对字符串进行硬编码 但设计者不喜欢这样抛出错误 我明白为什么这行不通
  • 即使没有异步,CallContext.LogicalGetData 也会恢复。为什么?

    我注意到CallContext LogicalSetData LogicalGetData不按照我期望的方式工作 内部设置的值async方法得到恢复即使没有异步或任何类型的线程切换 无论如何 这是一个简单的例子 using System u
  • Eigen 和 OpenMP:由于错误共享和线程开销而没有并行化

    系统规格 Intel Xeon E7 v3 处理器 4 插槽 16 核 插槽 2 线程 核心 Eigen 系列和 C 的使用 以下是代码片段的串行实现 Eigen VectorXd get Row const int j const int
  • 如何使用 ASP.NET Core 获取其他用户的声明

    我仍在学习 ASP NET Core 的身份 我正在进行基于声明的令牌授权 大多数示例都是关于 当前 登录用户的 就我而言 我的 RPC 服务正在接收身份数据库中某个用户的用户名和密码 我需要 验证是否存在具有此类凭据的用户 获取该用户的所
  • C++中判断unicode字符是全角还是半角

    我正在编写一个终端 控制台 应用程序 该应用程序应该包装任意 unicode 文本 终端通常使用等宽 固定宽度 字体 因此要换行文本 只需计算字符数并观察单词是否适合一行并采取相应的操作 问题是 Unicode 表中的全角字符在终端中占用了
  • 是否使用 C# 数据集? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我对 C 中的数据集概念有点困惑 编码 ASP NET 站点 但这并不重要 在我的阅读中 我了解到它们 本质上 用作我的应用程序和我的
  • 在 .NET MAUI 中实现 TouchTracking

    我一直致力于将我们的应用程序从 Xamarin Forms 迁移到 NET MAUI 我们的应用程序几乎没有绘图功能 用户可以用手指进行绘图 我们用了TouchTrackingXamarin Forms 中的 nuget 包 但与 NET
  • 如果输入被重定向则执行操作

    我想知道如果我的输入被重定向 我应该如何在 C 程序中执行操作 例如 假设我有已编译的程序 prog 并且我将输入 input txt 重定向到它 我这样做 prog lt input txt 我如何在代码中检测到这一点 一般来说 您无法判
  • C++ - 多维数组

    处理多维数组时 是否可以为数组分配两种不同的变量类型 例如你有数组int example i j 有可能吗i and j是两种完全不同的变量类型 例如 int 和 string 听起来您正在寻找 std vector
  • C++ 对象用 new 创建,用 free() 销毁;这有多糟糕?

    我正在修改一个相对较大的 C 程序 不幸的是 并不总是清楚我之前的人使用的是 C 还是 C 语法 这是在一所大学的电气工程系 我们 EE 总是想用 C 来做所有事情 不幸的是 在这种情况下 人们实际上可以逃脱惩罚 但是 如果有人创建一个对象
  • 在 System.Type 上使用条件断点时出错

    这是函数 public void Init System Type Type this Type Type BuildFieldAttributes BuildDataColumns FieldAttributes 我在第一行设置了一个断点
  • 是否允许全局静态标识符以单个 _ 开头?

    换句话说 可能static 文件范围 全局变量恰好以一个下划线开头 而不会产生与 C 实现发生名称冲突的可能性 https www gnu org software libc manual html node Reserved Names
  • 如何在 C 中将 char 连接到 char* ?

    我怎样才能前置char c to char myChar 我有c值为 A and myChar值为 LL 我怎样才能前置c to myChar使 ALL 这应该有效 include

随机推荐