如何阅读C声明?

如何阅读C声明?,c,typedef,cdecl,C,Typedef,Cdecl,我听说过一些方法,但没有一种是行之有效的。就我个人而言,我尽量避免使用C语言中的复杂类型,并尝试将它们分解为组件typedef 我现在面临着维护一个所谓的“三星级程序员”的一些遗留代码,我很难阅读一些***代码[] 如何阅读复杂的C声明 在我做C时,我使用了一个名为“cdecl”的程序。它似乎在UbuntuLinux的cutils或cdecl包中,并且可能在其他地方提供。一个词: 该死,被打败了15秒 Cdecl(和c++decl)是一个用于编码和解码c(或c++)类型声明的程序 常见的可读性问

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

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


如何阅读复杂的C声明

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

一个词:

该死,被打败了15秒

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


常见的可读性问题包括和这样一个事实,希望这对一些人有所帮助


在任何情况下,只要你理解了这些声明,也许你就可以找到一种方法来简化它们,让下一个人更容易阅读。

本文解释了一个相对简单的7条规则,如果你发现自己想要或需要手动阅读任何C声明,它将允许你阅读:

  • 查找标识符。这是你的出发点。在一张纸上写上“将标识符声明为”
  • 向右看。如果没有任何内容,或者有右括号“)”,请转至步骤4
  • 您现在位于数组(左括号)或函数(左括号)描述符上。可能有一个序列,以不匹配的右括号或声明符结尾(分号或“=”表示初始化)。对于每个这样的描述符,从左到右读取:

    • 如果是空数组“[]”,则写入“数组”
    • 如果数组的大小为,请写入“数组大小为”
    • 如果函数为“()”,则写“函数返回”
    在不匹配的括号或声明符的末尾停止,以先到者为准

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

  • 如果您对某个工具还满意,那么我建议您使用该程序
    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中的声明”。我通常使用有时被称为“右手顺时针规则”的规则。 事情是这样的:

    • 从标识符开始
    • 到它的右边去
    • 然后顺时针移动到左侧
    • 顺时针移动到右侧
    • 只要声明未被完全解析,就执行此操作
    还有一个需要注意的元规则:

    • 如果有括号,请在移出前完成每一级括号
    在这里,“去”和“移动”某处意味着在那里阅读符号。规则如下:

    • *
      -指向
    • ()
      -函数返回
    • (int,int)
      -函数获取两个int并返回
    • int
      char
      等-
      int
      char
    • []
      -数组
    • [10]
      -十位数组
    • 等等
    因此,例如,
    int*(*xyz[10])(int*,char)
    被解读为:

    xyz是一个

    十进位

    指向

    函数获取int*和char并返回

    整数*


    自动化解决方案是cdecl

    一般来说,您可以按使用方式声明变量。例如,您取消引用指针p,如下所示:

    char c = * p 字符c=*p 您以类似的方式声明它:

    char * p; char*p; 毛茸茸的函数指针也是如此。让我们将f声明为良好的旧“指向函数的指针返回指向int的指针”,并声明一个外部声明只是为了好玩。它是指向函数的指针,因此我们从以下内容开始:

    extern * f(); extern*f(); 它返回一个指向int的指针,所以在前面的某个地方有

    extern int * * f(); // XXX not quite yet extern int**f();//XXX还没有 现在哪一个是正确的关联性?我永远记不起来了,所以用一些括号

    extern (int *)(* f)(); 外部(内部*)(*f)();
    按照您使用它的方式声明它。

    刚刚在“”中遇到一个有启发性的部分:

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

    inti,*pi,**ppi;

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

    intf(),*f(),(*f)(;

    声明返回整数的函数、返回整数指针的函数、返回整数的函数指针

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

    声明一个指向整数的指针数组和一个指向整数数组的指针