gpt4 book ai didi

c - 为什么 C 中的箭头 (->) 运算符存在?

转载 作者:太空狗 更新时间:2023-10-29 16:13:41 25 4
gpt4 key购买 nike

点( . )运算符用于访问结构的成员,而 C 中的箭头运算符( -> )用于访问由相关指针引用的结构的成员。

指针本身没有任何可以用点运算符访问的成员(它实际上只是一个描述虚拟内存中位置的数字,因此它没有任何成员)。因此,如果我们只是定义点运算符以在指针上使用指针时自动取消引用指针(编译器在编译时已知的信息 afaik),则不会有歧义。

那么为什么语言创建者决定通过添加这个看似不必要的运算符来使事情变得更加复杂?什么是重大设计决策?

最佳答案

我会将您的问题解释为两个问题:1) 为什么 ->甚至存在,以及 2) 为什么 .不会自动取消引用指针。这两个问题的答案都有历史渊源。

为什么->甚至存在?

在 C 语言的最早版本之一(我将其称为“C Reference Manual”的 CRM,它于 1975 年 5 月随第 6 版 Unix 一起提供)中,运算符 ->具有非常排他的含义,不是 * 的同义词和 .组合

CRM 所描述的 C 语言在许多方面与现代 C 语言有很大不同。在 CRM 结构成员中实现了字节偏移的全局概念,可以将其添加到任何地址值而没有类型限制。 IE。所有 struct 成员的所有名称都具有独立的全局含义(因此,必须是唯一的)。例如,您可以声明

struct S {
int a;
int b;
};

和姓名 a将代表偏移量 0,而名称 b将代表偏移量 2(假设 int 类型的大小为 2 且没有填充)。该语言要求翻译单元中所有结构的所有成员具有唯一的名称或代表相同的偏移值。例如在同一个翻译单元中,您可以另外声明
struct X {
int a;
int x;
};

这样就可以了,因为名称 a将始终代表偏移量 0。但是这个额外的声明
struct Y {
int b;
int a;
};

将正式无效,因为它试图“重新定义” a作为偏移量 2 和 b作为偏移量 0。

这就是 ->的地方运算符进来了。由于每个结构成员名称都有其自给自足的全局含义,因此该语言支持这样的表达式
int i = 5;
i->b = 42; /* Write 42 into `int` at address 7 */
100->a = 0; /* Write 0 into `int` at address 100 */

第一个赋值被编译器解释为“获取地址 5,向其添加偏移量 2 并将 42 分配给结果地址处的 int 值”。 IE。以上将分配 42int地址处的值 7 .注意这里使用 ->不关心左侧表达式的类型。左侧被解释为右值数字地址(无论是指针还是整数)。
* 不可能有这种诡计和 .组合。你做不到
(*i).b = 42;

*i已经是一个无效的表达式。 *运算符,因为它与 . 分开, 对其操作数施加了更严格的类型要求。为了提供解决此限制的能力,CRM 引入了 ->运算符,它独立于左侧操作数的类型。

正如 Keith 在评论中指出的, -> 之间的差异和 * + .组合就是CRM在7.1.8中所说的“放宽要求”:除了放宽要求 E1为指针类型,表达式 E1−>MOS完全等同于 (*E1).MOS
后来,在 K&R C 中,对 CRM 中最初描述的许多功能进行了重大修改。完全删除了“结构成员作为全局偏移标识符”的想法。以及 -> 的功能运算符变得与 * 的功能完全相同和 .组合。

为什么不能.自动取消引用指针?

同样,在 CRM 版本的语言中, . 的左操作数运算符必须是左值。这是对该操作数的唯一要求(这就是它与 -> 不同的原因,如上所述)。请注意,CRM 不需要 . 的左操作数。有一个结构类型。它只是要求它是一个左值,任何左值。这意味着在 C 的 CRM 版本中您可以编写这样的代码
struct S { int a, b; };
struct T { float x, y, z; };

struct T c;
c.b = 55;

在这种情况下,编译器会写 55int值位于连续内存块中的字节偏移量 2 处,称为 c ,即使输入 struct T没有名为 b 的字段.编译器不会关心 c 的实际类型根本。它所关心的只是 c是一个左值:某种可写的内存块。

现在请注意,如果你这样做
S *s;
...
s.b = 42;

该代码将被认为是有效的(因为 s 也是一个左值)并且编译器将简单地尝试将数据写入指针 s本身,在字节偏移量 2 处。不用说,这样的事情很容易导致内存溢出,但语言并不关心这些问题。

IE。在该版本的语言中,您提出的关于重载运算符的想法 .对于指针类型不起作用:运算符 .与指针一起使用时(与左值指针或任何左值一起使用)已经具有非常具体的含义。毫无疑问,这是非常奇怪的功能。但当时它就在那里。

当然,这个奇怪的功能并不是反对引入重载 . 的强有力的理由。在 C - K&R C 的返工版本中用于指针的运算符(如您所建议的)。但它还没有完成。也许当时有一些必须支持的用 C 的 CRM 版本编写的遗留代码。

(1975 C 引用手册的 URL 可能不稳定。另一个副本,可能有一些细微差别,是 here 。)

关于c - 为什么 C 中的箭头 (->) 运算符存在?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13366083/

25 4 0
文章推荐: c - size_t 与 uintptr_t
文章推荐: c - bool 是原生 C 类型吗?
文章推荐: c - 为什么 0 < -0x80000000?
文章推荐: C 默认参数
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com