当前位置:  开发笔记 > 编程语言 > 正文

为什么C区分 - >和.?

如何解决《为什么C区分->和.?》经验,为你挑选了7个好方法。

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.xs.x,或者为什么保持强制区别,否则有用吗?



1> CB Bailey..:

我不认为你说的话有什么疯狂的.使用.的指针结构将工作.

但是,我喜欢这样一个事实:指向结构和结构的指针被区别对待.

它给出了关于操作的一些背景以及可能成本高昂的线索.

考虑一下这个片段,假设它处于一个相当大的函数中间.

s.c = 99;
f(s);

assert(s.c == 99);

目前我可以说这s是一个结构.我知道它将被完全复制以用于呼叫f.我也知道断言不能解雇.

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

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


目前你不能重载`.`运算符,但现在这并不重要.可能重要的是类可以像指针一样操作,因为你可以拦截` - >`运算符来返回指向'代理'对象的指针,但是你也可以调用对象本身的东西(例如`. reset()`将其设置为null或其他东西).失去`.`和` - >`之间的分歧会阻止它工作.
@William Pursell:Touche,我知道当我写这篇文章的时候,可能会有一个不成功的角落案例.我实际上想知道是否有一个类型,你可以分配一个int(99),然后当提升回int时不会比较等于99,但是自引用(或全局/静态实例!)也会工作.

2> cletus..:

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

p->x = 3;

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

p.x = 3;

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



3> Norman Ramse..:

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


咦?这与我编写的编译器数量有什么关系?您声称C具有明确的成本模型.我已经证明C成本模型并不是绝对明确的,而且,与OP提出的完全相同的"隐含性"*已经存在于语言中*.现在你似乎试图切换主题.为什么?那有什么意义呢?这是一个非常小的问题,但你表现得好像我的正确是以某种方式侮辱你.Sheesh ...... :)
好点子.并且在现代架构上,该内存引用可能非常昂贵,假设需要从主内存中获取数据,其成本可能是访问寄存器的1000倍.
@Norman Ramsey:OP提出的隐式内存引用与`[]`和`()`运算符中潜在的隐式内存引用具有相同的性质,正如我在回答中所述.当你使用`[]`运算符时,你无法从使用"真实"数组或指针对象(后者需要额外的内存引用)的语法中看到.所以不,这部分成本模型在C中通常不明确.而OP提出的建议并没有超越C成本模型"隐含性"的传统边界.

4> AnT..:

好吧,如果你真的想在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倍,以涵盖结构:)



5> 小智..:

好吧,肯定会出现这样的情况:

(*item)->elem

(我曾经在某些程序中发生过),如果你写了类似的东西

item.elem

意思是,如果elem是struct item的元素,或者item指向的struct的元素,或者是指向列表中的元素的struct的元素,可能会让人感到困惑.迭代器项,依此类推.

所以是的,当使用指向结构的指针时,它确实使事情更清晰一些.



6> DigitalRoss..:

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

它不仅可以,而且是现代风格.Java和Go都只是使用..因为不适合寄存器的所有内容都是某个级别的引用,所以事物指向事物的指针之间的区别肯定有点武断,至少在你进入函数调用之前.

第一个进化步骤是使取消引用运算符后缀,某些dmr曾经暗示他在某些方面偏好.Pascal这样做,所以它有p^.field.唯一的原因,甚至有一个->运营商,因为它是愚蠢的有键入(*p).fieldp[0].field.

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

我认为()用于函数调用和[]数组下标是错误的.为什么不允许不同的实现导出不同的抽象?

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

C真正需要的是编写不安全代码的方法并不是那么快.我不介意在C工作,但项目经理不喜欢他们的可靠性由他们最坏的人决定,并且C真正需要的是一种安全的方言,比如Cyclone,或者像Go这样的东西.



7> Noctis Skyto..:

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

推荐阅读
mobiledu2402851377
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有