OK,这是没有严重的后果,但它被窃听我一会:有没有为区分的理由->
和.
运营商?
当然,当前的规则是.
作用于结构,并->
作用于指向结构(或联合)的指针.但这是它在实践中的运作方式.让我们s
成为包含元素的结构x
,让它ps
成为指向同一形式的结构的指针.
如果你写
s->x
编译器会以这种方式吐出警告
你的意思是sx请重新输入并重新编译.
如果你写
ps.x
编译器会以这种方式吐出警告
你的意思是ps-> x.请重新输入并重新编译.
因为编译器知道两者的类型s
并且ps
在编译时,它具有解释正确运算符所需的所有信息.我怀疑这不像其他警告(如缺少分号),因为没有关于正确修复的含糊不清.
所以这是对C1x标准委员会的一个假设性提议(由于ISO处于保守状态,因此永远不会被考虑):
给定表达式lhs.rhs,如果lhs是结构或联合类型,则表达式应引用名为rhs的lhs元素.如果lhs是指向结构或-union的类型,则应将其解释为(*lhs).rhs.
这肯定会拯救我们所有的时间,并且让人们更容易学习C [并且我已经教过足够的C来说明学习者发现->
事情要么令人困惑或烦恼.]
甚至有先例,C做了一些类似的事情.例如,对于实现的原因,函数声明总是被转换为指针到功能,所以f(x,y)
和(*f)(x,y)
将两者的工作,无论是否f
被宣布为一个函数或函数指针.
所以,我的问题是:这个提案有什么问题?你能想到的例子,其中会有致命之间的歧义ps.x
和s.x
,或者为什么保持强制区别,否则有用吗?
我不认为你说的话有什么疯狂的.使用.
的指针结构将工作.
但是,我喜欢这样一个事实:指向结构和结构的指针被区别对待.
它给出了关于操作的一些背景以及可能成本高昂的线索.
考虑一下这个片段,假设它处于一个相当大的函数中间.
s.c = 99; f(s); assert(s.c == 99);
目前我可以说这s
是一个结构.我知道它将被完全复制以用于呼叫f
.我也知道断言不能解雇.
如果.
允许使用指向struct的指针,我不会知道任何这些,并且断言可能会触发,f
可能会将s.c
(错误s->c
)设置为其他内容.
另一个缺点是它会降低与C++的兼容性.C++允许->
类重载,以便类可以像"指针"一样.重要的是.
,->
行为和行为不同..
与指向结构的指针一起使用的"新"C代码不再可能不被C++代码接受.
那么显然没有任何含糊不清或无法提出建议.唯一的问题是如果你看到:
p->x = 3;
你知道p
是一个指针,但如果你允许:
p.x = 3;
在那种情况下,你实际上并不知道,这可能会产生问题,特别是如果你以后强制转换指针并使用错误的间接级别.
C编程语言的一个显着特征(与其相对的C++相反)是成本模型非常明确.该点与箭头不同,因为箭头需要额外的内存引用,并且C非常小心地使源代码中的内存引用数量明显.
好吧,如果你真的想在C语言的规范中引入这种功能,那么为了使它与语言的其余部分"融合",逻辑上要做的就是将"衰变"的概念扩展到指针"结构类型.你自己用函数和函数指针做了一个例子.它以这种方式工作的原因是因为C中的函数类型衰减到所有上下文中的指针类型,除了sizeof
和一元运算&
符.(同样的事情发生在阵列上,BTW.)
因此,为了实现类似于你建议的东西,我们可以引入"struct-to-pointer decay"的概念,它的工作方式与C中所有其他"衰变"的方式完全相同(即array-to -pointer decay和function-to-pointer decay)工作:当T
在表达式中使用类型的struct对象时,其类型会立即衰减为T*
- 指向struct对象开头的指针 - 除非它是操作数sizeof
或一元&
.一旦为结构引入了这样的衰减规则,您就可以使用->
运算符来访问struct元素,无论您是在左侧是否有指向struct或struct本身的指针..
在这种情况下,操作员将变得完全没必要(除非我遗漏了某些东西),你总是->
只使用->
.
以上再次说明,如果它是以C语言的精神实现的,那么这个功能在我看来会是什么样子.
但我会说(同意Charles所说的),使用指向结构的指针的代码和使用结构本身的代码之间的视觉区别的丢失并不是完全可取的.
PS这种结构衰变规则的一个明显的负面后果是,除了当前的新手大军无私地相信"数组只是常数指针"之外,我们还有一群新手无私地相信"结构对象只是常量指针" ".而克里斯托雷克的阵列常见问题解答必须大约1.5-2倍,以涵盖结构:)
好吧,肯定会出现这样的情况:
(*item)->elem
(我曾经在某些程序中发生过),如果你写了类似的东西
item.elem
意思是,如果elem是struct item的元素,或者item指向的struct的元素,或者是指向列表中的元素的struct的元素,可能会让人感到困惑.迭代器项,依此类推.
所以是的,当使用指向结构的指针时,它确实使事情更清晰一些.
它不仅可以,而且是现代风格.Java和Go都只是使用.
.因为不适合寄存器的所有内容都是某个级别的引用,所以事物和指向事物的指针之间的区别肯定有点武断,至少在你进入函数调用之前.
第一个进化步骤是使取消引用运算符后缀,某些dmr曾经暗示他在某些方面偏好.Pascal这样做,所以它有p^.field
.唯一的原因,甚至有是一个->
运营商,因为它是愚蠢的有键入(*p).field
或p[0].field
.
是的,它会起作用.它甚至会更好,因为它在更高的抽象层次上工作.一个人真的应该能够尽可能多地进行更改而不需要更改下游代码,这在某种意义上说是更高级语言的全部要点.
我认为()
用于函数调用和[]
数组下标是错误的.为什么不允许不同的实现导出不同的抽象?
但没有太多理由做出改变.C程序员不太可能反复缺乏一个语法糖扩展,这种扩展可以在表达式中保存一个字符,并且无论如何都很难使用它,因为如果普遍采用它就不会立即使用它.请记住,当标准委员会变得流氓时,他们最终会向空房子讲道.它们需要世界编译开发人员的自愿合作和协议.
C真正需要的是编写不安全代码的方法并不是那么快.我不介意在C工作,但项目经理不喜欢他们的可靠性由他们最坏的人决定,并且C真正需要的是一种安全的方言,比如Cyclone,或者像Go这样的东西.
如果有的话,当前语法允许代码的读者知道代码是否使用指针或实际对象.事先不了解代码的人会更好地理解它.