你如何阅读C声明?

我听说过一些方法,但没有一个卡住了。 我个人尽量避免在C中的复杂types,并尝试将它们分解成组件types定义。

我现在正在维护一个所谓的“三星级程序员”的遗留代码,而且我正在阅读一些***代码[] []。

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

这篇文章解释了一个相对简单的7条规则,可以让你阅读任何C声明,如果你发现自己想要或者需要手动这样做: http : //www.ericgiguere.com/articles/reading-c-declarations.html

  1. find标识符。 这是你的出发点。 在一张纸上写上“声明标识符为”。
  2. 向右看。 如果没有任何东西,或者有右括号“)”,那么转到步骤4。
  3. 您现在位于数组(左括号)或函数(左括号)描述符上。 可能有这些序列,以无匹配的右括号或声明符的末尾(分号或用于初始化的“=”)结束。 对于每个这样的描述符,从左到右阅读:

    • 如果一个空数组“[]”,写“数组”
    • 如果一个数组的大小,写“数组大小”
    • 如果函数“()”写入“函数返回”

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

  4. 返回到开始位置,向左看。 如果什么都没有,或者有左括号“(”,转到步骤6。
  5. 您现在位于指针描述符“*”上。 在左边可能有一个序列,结尾是一个不匹配的左括号“(”或声明符的开始。从右向左读取,每个指针描述符写入“指向”。停在不匹配的括号处或宣布者的开始,以先到者为准。
  6. 在这一点上,你有一个加括号的expression式或完整的声明。 如果你有一个加括号的expression式,把它作为你的新起点,并返回到步骤2。
  7. 写下types说明符。 停止。

如果你对一个工具很好,那么我第二个build议使用程序cdecl : http : cdecl

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

  • 从标识符开始。
  • 转到它的右侧。
  • 然后顺时针移动到左侧。
  • 顺时针移动到右侧。
  • 只要声明没有完全parsing,就这样做。

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

  • 如果有括号,在移出之前完成括号的每个级别。

在这里,“去”和“移动”的地方意味着在那里读取符号。 规则是:

  • * – 指向
  • () – 函数返回
  • (int, int) – 取两个整数并返回的函数
  • intchar等 – intchar
  • [] – 数组
  • [10] – 十个数组
  • 等等

所以,例如, int* (*xyz[10])(int*, char)被读为:

xyz是一个

十个数组

指向

函数采取一个int *和一个字符,并返回

一个int *

一个字: cdecl

该死,被殴打15秒!

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

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

当我做C时,我使用了一个名为“cdecl”的程序。 看来它在Ubuntu Linux中的cutils或cdecl包中,而且可能在其他地方可用。

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深度秘密”一书中有一整章,名为“在C中解读声明”

问候弗里德里希

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

常见的可读性问题包括函数指针和数组实际上是指针 ,而multidimensional array实际上是单维数组(实际上是指针)。 希望有一些帮助。

无论如何,只要你理解了声明,也许你可以想出一种方法来简化它们,使它们对下一个人更具可读性。

自动化解决scheme是cdecl。

一般来说,你使用它的方式声明一个variables。 例如,您可以像下面那样取消引用指针p:

 char c = * p

你用类似的方式声明它:

 char * p;

多毛的函数指针也一样。 让我们声明f是很好的“返回指向int的函数的指针”,而一个外部声明只是有趣的。 这是一个函数的指针,所以我们从下面开始:

 extern * f();

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

 extern int * * f();  // XXX还不完全

现在哪个是正确的结合? 我永远不会记住,所以用一些括号。

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

声明它的使用方式。

从右到左阅读。

 ***code[][] 
  • 代码[] []是一个multidimensional array
  • * code [] []是一个multidimensional array指针
  • ** code [] []是一个指针的multidimensional array指针
  • *** code [] []是一个多指针数组指针,指向一个指针

刚刚在“ C语言的发展 ”中遇到了一个照明部分:

对于这种组合types的每个对象,已经有一种方法来提及底层对象:索引数组,调用函数,在指针上使用间接运算符。 类比推理导致名称的声明语法镜像名称通常出现的expression式语法的名称。 从而,

int i, *pi, **ppi;

声明一个整数,一个指向一个整数的指针,一个指向一个整数指针的指针。 这些声明的语法反映了当在expression式中使用时,我,* pi和** ppi都会产生一个inttypes。 同样的,

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

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

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

声明一个指向整数的指针数组,以及一个指向整型数组的指针。 在所有这些情况下,variables的声明类似于在声明头部命名的expression式中的用法。