为啥必须在 C 中向后读取指针声明? [关闭]

Posted

技术标签:

【中文标题】为啥必须在 C 中向后读取指针声明? [关闭]【英文标题】:Why do you have to read a pointer declaration backwords in C? [closed]为什么必须在 C 中向后读取指针声明? [关闭] 【发布时间】:2017-06-27 14:41:07 【问题描述】:

在 C 语言中,对于简单的情况,似乎要读取指针声明,您必须向后进行。例如:

int n; 
int *p = &n; // p is a pointer to int
int *const np = &n; // np is a const pointer to int
int *const *npp = &np; //npp is a (non-const) pointer to const pointer to (non-const) int

即使解析类型声明的正确方法是通过所谓的the spiral rule,如果解析规则不同以便以另一种方式阅读简单的指针声明,那不是更容易吗? 例如:

int n;
*int p = &n; // p is a pointer to int
const *int np = &n; // np is a const pointer to int
*const *int npp = &np; // npp is a (non-const) pointer to const pointer to (non-const) int

所以我的问题又来了:这种设计选择背后的原则是什么?是什么促使语言设计者选择这种特殊的方式来声明类型。

【问题讨论】:

我认为这里唯一没有意见的答案是“因为它在历史上是这样定义的”。 无主见的答案是你一般不会倒着读,只有在简单的情况下。规范的解决方案是spiral rule。 C 声明语法实际上相当复杂 - 对象或函数的类型完全由 声明说明符(类型说明符、类型限定符、存储类说明符)的组合指定和一个声明符(标识符,加上*()[] 运算符的某种组合)。指针性、数组性和函数性在声明器中指定,与类型说明符分开。这使您只需几次击键即可创建一些相当复杂的类型;缺点是这些声明会很快引起注意。 如果 Ritchie 使用间接运算符后缀而不是一元后缀,那就太好了 - T a[N]*[M]*; 更容易理解为“a 是指向数组的指针数组指向T" 的指针比T *(*a[M])[N]; 多,我们在阅读声明时不必来回反弹。但是,他没有。 @elFreak:啊。在这种情况下,这是因为 B 就是这样做的(这是因为 BCPL 就是这样做的,尽管使用不同的运算符)。 很多 C 的怪异之处可以追溯到B 和BCPL。您可能会发现感兴趣的 this article。 【参考方案1】:

除了语法。如果我没记错背后的想法:

int **a;

如下。您需要取消引用 a 两次才能获得一个 int。这样 const 位置也开始有意义了。

int * const * a;

这实质上意味着您需要取消引用一次才能获得指向 int 的 const 指针。


您可能会发现阅读 C 起源会很有趣且很有教育意义。 (PDP、B 等)

【讨论】:

解引用语法也可以更改。如果我们开始改变事情......那么用另一种语法证明一种语法并不是一种真正的方法。 @Eugene Sh.这就是我的看法。 C 是进化而不是革命创造的,因此它从它的前辈那里借用了一些语法。在我看来,有几个与语法相关的有争议的决定(如函数声明语法)。此外,C 的采用远早于它的标准化。然而,在我看来,那时的主要目标是创建一个接近现有实现的标准,否则它将是一种完全不同的语言并且不会被采用。之后,一切都是为了向后兼容。 那我们可以问问前辈们一样吗?我们在哪里停下来?这就是为什么这个问题可能会以基于意见的方式结束。 @EugeneSh。虽然您可能不喜欢它,但 C 的创建者确实设计了从表达式到类型声明的语言。遵循的指导原则是,变量的声明应该与它的使用相似。当然,他们可以将解引用运算符定义为后缀运算符,就像数组下标一样,但他们没有。无论他们为表达式中的运算符位置和优先级选择什么,都完全定义了声明的结构。 @EugeneSh。这是参考。作者:Dennis M. Ritchie 本人:heim.ifi.uio.no/inf2270/programmer/historien-om-C.pdf 在第 7 页上,您发现 a) 指针/数组/函数运算符确实早于 C(来自其无类型的前辈),并且 b) 他确实定义了声明语法以匹配表达式语法。【参考方案2】:

为什么必须在 C 中向后读取指针声明?

在我看来,很难说声明是否真正倒退了。

例如...

我注意到在过去几年中,我阅读头脑中的代码的方式发生了变化。

考虑以下代码:

int *p;

你是如何在脑海中阅读这段代码的?

我曾经读过:“p 是指向 int 的指针”。

现在我读到:“一个名为 p 的 int 指针”。

由于频繁使用该语言,我的大脑已经改变了将源代码翻译成英文的方式。这让我能够理解语法,而不会觉得它倒退了。

对我来说,感觉是向前,尽管这完全是主观的,并且会因人而异。

此外,可能有许多(口语)语言的源代码可以很好地翻译成口语句子,而无需更改顺序。

结论

声明是否感觉倒退取决于...

您的母语。 如何将代码翻译成您的母语。 声明的大小/复杂性。

【讨论】:

我同意。我不觉得宣言真的是倒退的。将声明视为整数指针而不是指向整数的指针对我来说非常有意义(尽管在语义上它们是相同的)。无论如何,这都是编译器的语法。通常,指针的大小都相同(4 到 8 个字节)。所以从这个意义上说,指针就是指针。编译器只是使用指针类型来验证代码中其他地方的使用情况。 您如何阅读声明char* (*arr)[42]?我肯定会称之为pointer to an array of 42 string pointers,从里到外阅读声明...... @cmaster 示例表明这不是向前或向后阅读的问题。读取 c 类型的正确方法是从内向外,甚至是顺时针,螺旋:c-faq.com/decl/spiral.anderson.html 还有一种“可以向右,必须向左”的技术:unixwiz.net/techtips/reading-cdecl.html @Tarc 是的,语法很快就会失控。我的回答只适用于非常小的情况。螺旋把戏太棒了。 @byxor,“失控”很好地描述了我猜这种情况:) 有时我发现自己需要重新研究如何正确阅读 C 类型声明。

以上是关于为啥必须在 C 中向后读取指针声明? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

C++ 读取访问冲突。 _Val 为 nullptr [关闭]

如何在Java中向后打印多个字符串

在c ++中向/从文件读取写入变量

允许用户在谷歌地图中向后移动标记位置并关闭窗口

我在删除指针时遇到读取访问冲突[关闭]

从内存中读取图像,知道指向内存的指针。 VC++ [关闭]