这些是C/C++能做的
服务器开发工程师、人工智能、云计算工程师、信息安全(黑客反黑客)、大数据 、数据平台、嵌入式工程师、流媒体服务器、数据控解、图像处理、音频视频开发工程师、游戏服务器、分布式系统、游戏辅助等
首先 a->b 的含义是 (*a).b,所以他们是不同的,不过的确->可以用 * 和 . 实现,不需要单独一个运算符。
从现代的标准化的C语义上来说,->可以用*和.的组合实现。但是,早期的C有一段时间的语义和现代的C的语义不太一样。
稍微有点汇编的基础的同学可能知道,在机器码和汇编的角度来看,不存在变量,不存在struct这种东西,只存在寄存器和一个叫做内存的大数组。所以变量,是C对内存地址的一个抽象,它代表了一个位置。
举个栗子,C里面我们写 a = b 其实在汇编的角度来看更像是 *A = *B 其中 A 和 B 各是两个内存地址,是指针。
好,以上是基本背景。基于这个背景我们讨论一下struct是什么,以及struct的成员是什么。
假设我们有:
从现代语义上讲 p 就是一个结构体对象,x 和 y 各是其成员。
从汇编的语义上讲,p 是一个不完整的地址,或者说,半个地址,再或者说,一个指向的东西是虚构出来的地址。而 x 和 y 各是在 Point 结构中的地址偏移量。也就是说,必须有 p和x 或者 p和y 同时出现,才形成一个完整的地址,单独的一个 p 没有意义。
早期的C就是在这样的模型上建立的。所以对早期的C而言,*pp 没有意义,你取得了一个struct,而这个struct不能塞在任何一个寄存器里,编译器和CPU都无法表达这个东西。这时候只有 p.x 和 p.y 有意义,它们有真实的地址。
早期的C就是这样一个看起来怪异的语义,而它更贴近机器的表达。
所以对早期的C而言,以下的代码是对的:
而以下代码是错的:
因为作为这个赋值的目标地址表达式的一部分,*pp,这个中间结果没法直译到机器码。所以对早期的C而言,对pp解引用的操作,必须和取成员的偏移的操作,这两者紧密结合起来变成一个单独的操作,其结果才有意义。
所以早期的C就发明了->,表示这两个操作紧密结合的操作。于是才能写:pp->x = 1;嗯,这就是它存在的历史原因。而这个历史原因现在已经不重要了,现代的符合标准的C编译器都知道 (*pp).x 和 pp->x 是等价的了。
说句题外话,C++里面还发明了 .* 和 ->* 这两个运算符(注意 ->* 不是单独的 -> 和 * 并排放的意思),关于为什么要发明这两个运算符,而不能直接说 a ->* b 的意思就是 a ->(*b),请读者们自己动脑想想,并在评论区留言。