【问题标题】:How do you read C declarations?你如何阅读 C 声明?
【发布时间】:2010-09-10 11:36:33
【问题描述】:

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

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

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

【问题讨论】:

  • 眼睛可以工作...但是如果您无法使用 Windows,我想您可以尝试讲述人。
  • 那么“三星级程序员”是否意味着他声明的所有类型中至少有三颗星?
  • 差不多,这是对一个糟糕的 c 程序员的老派侮辱
  • 这是一个 website 为您自动执行此操作!
  • 而且,对于***code[][],它返回syntax error :-) 不过,这是一个非常有用的网站。

标签: c typedef cdecl


【解决方案1】:

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

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

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

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

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

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

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

    xyz 是一个

    十个数组

    指针

    函数接受一个 int* 和一个 char 并返回

    一个整数*

    【讨论】:

    【解决方案3】:

    一个字:cdecl

    妈的,被揍了15秒!

    【讨论】:

    • 见我上面的评论。对于***code[][],它返回syntax error :-) 一个非常有用的网站,虽然
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        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.

        【讨论】:

          【解决方案7】:

          刚刚在“The Development of the C Language”中发现了一个有启发性的部分:

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

          int i, *pi, **ppi;

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

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

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

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

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

          【讨论】:

            【解决方案8】:

            还有一个Web-based version of cdecl,非常漂亮。

            【讨论】:

              【解决方案9】:

              常见的可读性问题包括 function pointersarrays are really pointers 以及 multidimensional arrays are really single dimension arrays (which are really pointers). 希望对一些人有所帮助。

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

              【讨论】:

                【解决方案10】:

                自动解决方案是 cdecl。

                一般来说,您声明一个变量的方式是您使用它的方式。例如,您取消引用指针 p,如下所示:

                字符 c = * p

                你以类似的方式声明它:

                字符 * p;

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

                外部 * f();

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

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

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

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

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-02-26
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-09-12
                  相关资源
                  最近更新 更多