为什么C区分 - >和。?

时间:2009-11-28 21:46:02

标签: c struct

好吧,这没有什么严重后果,但它一直困扰着我 while:是否有理由区分->.运算符?

当然,当前规则是.作用于结构,->作用于 指向结构的指针(或联合)。但这是它在实践中的运作方式。 让s成为包含元素x的结构,让ps成为指向同一表单结构的指针。

如果你写

s->x

编译器将以

的方式发出警告
  

你的意思是s.x.请重新输入并重新编译。

如果你写

ps.x

编译器将以

的方式发出警告
  

你的意思是ps-> x。请重新输入并重新编译。

因为编译器在编译时知道sps的类型,所以它具有解释正确运算符所需的所有信息。我怀疑这不像其他警告(如缺少分号),因为没有关于正确修复的含糊不清。

所以这是对C1x标准委员会的一个假设性建议(由于ISO处于保守的连续性,因此永远不会被考虑):

  

给定表达式lhs.rhs,如果lhs是结构或联合类型,       那么表达式应该引用名为rhs的lhs元素。       如果lhs是指向结构或-union的类型,那么这应该是       解释为(* lhs).rhs。

这肯定会拯救我们所有的时间,并且让人们更容易学习C [并且我已经教过足够的C来说明学习者发现->的事情要么令人困惑或烦恼。

甚至有先例,C做了一些类似的事情。例如,出于实现原因,函数声明总是强制转换为指向函数的指针,因此f(x,y)(*f)(x,y)都可以工作,无论f被声明为函数还是指向功能

所以,我的问题是:这个提案有什么问题?你能想到ps.xs.x之间存在致命歧义的例子,或者为什么强制性区分是否有用?

7 个答案:

答案 0 :(得分:37)

我不认为你所说的话有什么疯狂的。使用.指向结构的指针会起作用。

但是,我喜欢结构和结构的指针被区别对待的事实。

它给出了关于操作的一些背景以及可能很昂贵的线索。

考虑这个片段,想象它正处于一个相当大的功能中。

s.c = 99;
f(s);

assert(s.c == 99);

目前我可以说s是一个结构。我知道,对于f的调用,它将被完整复制。我也知道断言不能开火。

如果允许使用.指向struct的指针,我就不会知道任何内容,并且断言可能会触发,f可能设置s.c(错误s->c )到其他东西。

另一个缺点是它会降低与C ++的兼容性。 C ++允许类重载->,以便类可以像“指针”一样。 .->行为不同非常重要。使用带有结构指针的.的“新”C代码不再可能被C ++代码接受。

答案 1 :(得分:30)

那么显然没有任何含糊之处或无法提出建议。唯一的问题是如果你看到:

p->x = 3;

你知道p是一个指针,但如果你允许:

p.x = 3;

在那种情况下,你实际上并不知道,这可能会产生问题,特别是如果你后来转换指针并使用错误的间接级别。

答案 2 :(得分:27)

C编程语言的一个显着特征(与其相对的C ++相反)是成本模型非常明确。该点与箭头不同,因为箭头需要额外的内存引用,C非常小心地使源代码中的内存引用数量明显。

答案 3 :(得分:10)

好吧,如果你真的想在C语言的规范中引入那种功能,那么为了使它与语言的其余部分“融合”,合乎逻辑的做法是扩展“衰减指针“到结构类型。你自己用函数和函数指针做了一个例子。它以这种方式工作的原因是因为C中的函数类型衰减到所有上下文中的指针类型,除了sizeof和一元&运算符。 (同样的事情发生在阵列上,BTW。)

因此,为了实现类似于你建议的东西,我们可以引入“struct-to-pointer decay”的概念,它的工作方式与C中所有其他“衰变”完全相同(即工作:当在表达式中使用类型为T的结构对象时,其类型会立即衰减到类型T* - 指向开头的指针struct对象 - 除非它是sizeof或一元&的操作数。一旦为结构引入了这样的衰减规则,您可以使用->运算符来访问struct元素,无论您是在左侧是否有指向struct或struct本身的指针。在这种情况下,操作员.将变得完全不必要(除非我遗漏了某些内容),您始终使用->并且只使用->

以上再次说明,在我看来,如果它是以C语言的精神实现的,那么这个功能会是什么样子。

但我会说(同意Charles所说的),使用指向结构的指针的代码和使用结构本身的代码之间的视觉区别的丢失并不是完全可取的。

P.S。这种结构衰变规则的一个明显的负面后果是,除了当前的新手大军无私地相信“数组只是常数指针”之外,我们还有一群新手无私地相信“结构对象只是常量指针” 。而克里斯托雷克的阵列常见问题解答必须大约1.5-2倍才能覆盖结构:)

答案 4 :(得分:8)

嗯,肯定会出现像你这样复杂的事情:

(*item)->elem

(我曾经在某些程序中发生过),如果你写了像

这样的东西
item.elem

意味着上面的内容,elem是struct item的元素,或者item指向的struct的元素,或者是指向列表中的元素的struct的元素,可能会让人感到困惑通过迭代器项目,依此类推。

所以是的,当使用指向结构的指针时,它确实使事情更加清晰,& c。

答案 5 :(得分:7)

是的,没关系,但这不是C真正需要的

不仅可以,而且是现代风格。 Java和Go都只使用.。因为不适合寄存器的所有东西都在某种程度上是引用,所以 thing 指向的指针之间的区别肯定有点武断,至少在你获得之前打电话。

第一个进化步骤是使取消引用运算符后缀, dmr 一旦暗示他在某些方面首选。 Pascal这样做,所以它有p^.field。甚至 一个->运算符的唯一原因是因为必须输入(*p).fieldp[0].field才是傻瓜。

是的,它会起作用。它甚至会更好,因为它在更高的抽象层次上工作。一个真正的应该能够在不要求下游代码改变的情况下进行尽可能多的更改,这在某种意义上说是更高级语言的全部要点。

我认为使用()进行函数调用而使用[]进行数组下标是错误的。为什么不允许不同的实现导出不同的抽象?

但没有太多理由做出改变。 C程序员不太可能反复缺乏一个语法糖扩展,这种扩展可以在表达式中保存一个字符,并且无论如何都很难使用,因为如果普遍采用的话,它就不会立即使用。请记住,当标准委员会变得流氓时,他们最终会向空房子讲道。它们需要世界编译开发人员的自愿合作和协议。

C真正需要的是编写不安全代码的方式并不是那么快。我不介意在C中工作,但项目经理不喜欢他们的可靠性由他们最糟糕的人,有可能C真正需要的是一种安全的方言, like Cyclone ,或者 {{3} }。

答案 6 :(得分:5)

如果有的话,当前语法允许代码的读者知道代码是否使用指针或实际对象。事先不了解代码的人会更好地理解代码。