45

我听说过一些方法,但没有一个能成功。就我个人而言,我尽量避免使用 C 中的复杂类型,并尝试将它们分解为组件 typedef。

我现在面临维护一些来自所谓的“三星级程序员”的遗留代码,而且我很难阅读一些 ***code[][]。

您如何阅读复杂的 C 声明?

4

10 回答 10

35

本文解释了一个相对简单的 7 条规则,如果您发现自己想要或需要手动阅读任何 C 声明,它们将让您阅读:http ://www.ericgiguere.com/articles/reading-c-declarations.html

  1. 找到标识符。这是你的起点。在一张纸上,写下“将标识符声明为”。
  2. 向右看。如果那里什么都没有,或者有一个右括号“)”,则转到第 4 步。
  3. 您现在位于数组(左括号)或函数(左括号)描述符上。可能有一系列这些,以不匹配的右括号或声明符的结尾(分号或“=”用于初始化)结束。对于每个这样的描述符,从左到右阅读:

    • 如果一个空数组“[]”,写“数组”
    • 如果一个数组有大小,写“数组大小”
    • 如果是函数“()”,则写为“函数返回”

    停在不匹配的括号或声明符的末尾,以先到者为准。

  4. 回到起始位置,向左看。如果那里什么都没有,或者有一个左括号“(”,请转到第 6 步。
  5. 您现在位于指针描述符“*”上。左边可能有一系列这些,以不匹配的左括号“(”或声明符的开头结尾。从右到左读取,对于每个指针描述符写入“指针”。在不匹配的括号处停止或声明符的开始,以先到者为准。
  6. 此时,您有一个带括号的表达式或完整的声明符。如果您有一个带括号的表达式,请将其视为您的新起点并返回到步骤 2。
  7. 写下类型说明符。停止。

如果您对工具没问题,那么我建议您使用该程序cdecl: http: //gd.tuwien.ac.at/linuxcommand.org/man_pages/cdecl1.html

于 2008-09-18T01:07:15.647 回答
24

我通常使用有时被称为“右手顺时针规则”的东西。它是这样的:

  • 从标识符开始。
  • 走到它的右边。
  • 然后顺时针移动,来到左侧。
  • 顺时针移动并来到右侧。
  • 只要声明没有被完全解析,就这样做。

还有一个额外的元规则需要注意:

  • 如果有括号,请在搬出前完成每一级括号。

在这里,“去”和“移动”某处意味着阅读那里的符号。规则是:

  • *- 指向的指针
  • ()- 函数返回
  • (int, int)- 函数接受两个整数并返回
  • int, char, 等等 - int, char, 等等
  • []- 数组
  • [10]- 十个数组
  • 等等

因此,例如,int* (*xyz[10])(int*, char)读作:

xyz 是一个

十个数组

指向的指针

函数采用 int* 和 char 并返回

一个整数*

于 2008-09-18T07:14:11.333 回答
7

一个字:cdecl

妈的,被打了15秒!

于 2008-09-18T01:02:04.613 回答
4

Cdecl(和 c++decl)是一个用于编码和解码 C(或 C++)类型声明的程序。

http://gd.tuwien.ac.at/linuxcommand.org/man_pages/cdecl1.html

于 2008-09-18T01:03:19.323 回答
3

回到我做 C 的时候,我使用了一个名为“cdecl”的程序。它似乎在 Ubuntu Linux 中的 cutils 或 cdecl 包中,并且可能在其他地方可用。

于 2008-09-18T01:01:49.357 回答
3

cdecl 提供了一个命令行界面,让我们试一试:

cdecl> explain int ***c[][]
declare c as array of array of pointer to pointer to pointer to int

另一个例子

explain int (*IMP)(ID,SEL) 
declare IMP as pointer to function (ID, SEL) returning int

然而,在“C Deep Secrets”一书中有一整章,名为“Unscrambling declarations in C.

于 2008-09-18T06:41:51.503 回答
1

刚刚在“ C语言的发展”中看到了一个启发性的部分:

对于这种组合类型的每个对象,已经有一种方法可以提及底层对象:索引数组、调用函数、在指针上使用间接运算符。类比推理导致名称的声明语法反映了名称通常出现的表达式语法。因此,

int i, *pi, **ppi;

声明一个整数,一个整数指针,一个指向整数指针的指针。这些声明的语法反映了 i、*pi 和 **ppi 在表达式中使用时都会产生 int 类型的观察结果。相似地,

int f(), *f(), (*f)();

声明一个返回整数的函数,一个返回指向整数的指针的函数,一个指向返回整数的函数的指针;

int *api[10], (*pai)[10];

声明一个指向整数的指针数组和一个指向整数数组的指针。在所有这些情况下,变量的声明类似于它在表达式中的用法,其类型是在声明开头指定的类型。

于 2009-12-05T07:11:02.760 回答
1

还有一个非常漂亮的基于 Web 的 cdecl 版本。

于 2011-05-27T04:15:13.930 回答
0

自动化解决方案是 cdecl。

通常,您以使用它的方式声明变量。例如,您取消引用指针 p,如下所示:

字符 c = * p

你以类似的方式声明它:

字符 * p;

毛茸茸的函数指针也是如此。让我们将 f 声明为旧的“指向函数的指针,返回指向 int 的指针”,以及一个外部声明,只是为了好玩。它是一个指向函数的指针,所以我们从:

外部 * f();

它返回一个指向 int 的指针,所以前面的某个地方有

外部 int * * f(); // XXX 还没有

现在哪个是正确的关联性?我不记得了,所以用一些括号。

extern (int *)(* f)();

以您使用它的方式声明它。

于 2008-09-20T03:48:36.060 回答
0

常见的可读性问题包括函数指针数组实际上是指针这一事实,以及多维数组实际上是一维数组(实际上是指针)。希望对一些人有所帮助。

无论如何,只要您确实理解了声明,也许您可​​以找到一种简化它们的方法,以使它们对下一个人来说更具可读性。

于 2008-09-18T01:04:35.590 回答