复杂的C声明

时间:2013-02-27 11:57:32

标签: c++ c function-pointers declaration

我刚刚在互联网上查看了一些代码,发现了这个:

float * (*(*foo())[SIZE][SIZE])()

我如何阅读此声明?是否有一套特定的规则来阅读这些复杂的声明?

8 个答案:

答案 0 :(得分:119)

我暂时没有做到这一点!

foo开始,然后向右走。

float * (*(* <子> foo() )[SIZE][SIZE])()

  

foo是一个没有参数的函数......

不能正确,因为它有一个右括号。向左走:

float * (*( <子> * foo() )[SIZE][SIZE])()

  

foo是一个没有参数返回指针的函数

不能再往前走了,所以让我们越过括号再往右走吧

float * (* <子> (* foo()) [SIZE][SIZE])() float * (* <子> (* foo())[SIZE] [SIZE])() float * (* <子> (* foo())[SIZE][SIZE] )()

  

foo是一个没有参数的函数,返回一个指向SIZE SIZE数组数组的指针......

达到关闭括号,再次离开以达到指针符号:

float * ( <子> *(* foo())[SIZE][SIZE] )()

  

foo是一个没有参数的函数,返回指向SIZE指针的SIZE数组数组的指针...

再次左括号,所以我们将它交叉并再次右转:

float * <子> ( *(* foo())[SIZE][SIZE]) () float * <子> ( *(* foo())[SIZE][SIZE])()

  

foo是一个没有参数的函数,返回指向SIZE指针的SIZE数组数组的指针,指向没有参数的函数......

离开到最后

<子> float * ( *(* foo())[SIZE][SIZE])()

  

foo是一个没有参数的函数,返回指向SIZE指针的SIZE数组数组的指针,该指针指向函数,没有参数返回指向float的指针


无论是谁写的,请教他使用typedef

// Function that returns a pointer to float
typedef float* PFloatFunc ();

// Array of pointers to PFloatFunc functions
typedef PFloatFunc* PFloatFuncArray2D[SIZE][SIZE];

// Function that returns a pointer to a PFloatFuncArray2D
PFloatFuncArray2D* foo();

答案 1 :(得分:98)

标准规则:找到最左边的标识符并逐步解决,记住[]之前()*绑定:

            foo                      -- foo
            foo()                    -- is a function
           *foo()                    -- returning a pointer
          (*foo())[SIZE]             -- to a SIZE-element array
          (*foo())[SIZE][SIZE]       -- of SIZE-element arrays
         *(*foo())[SIZE][SIZE]       -- of pointers
        (*(*foo())[SIZE][SIZE])()    -- to functions
      * (*(*foo())[SIZE][SIZE])()    -- returning pointers
float * (*(*foo())[SIZE][SIZE])();   -- to float

因此,假设您有一堆函数返回指向float的指针:

float *quux();
float *bar();
float *bletch();
float *blurga();

假设您想将它们存储在2x2表中:

float *(*tab[SIZE][SIZE])() = {quux, bar, bletch, blurga};

tab是一个SIZE x SIZE指针数组,指向返回float指针的函数。

现在让我们决定我们想要一个函数来返回指向该表的指针:

float *(*(*foo())[SIZE][SIZE])()
{
  static float *(*tab[SIZE][SIZE])() = {quux, bar, bletch, blurga};
  return &tab;
}

请注意,您可以使用多个函数来构建不同函数的表,或者以不同方式组织相同的函数:

float *(*(*qwerbl())[SIZE][SIZE])()
{
  static float *(*tab[SIZE][SIZE])() = {blurga, bletch, bar, quux};
  return tab;
}

这是我能想到做这样的事情的唯一原因。你不应该经常在野外看到这样的类型(尽管它们偶尔会突然出现,而且我也写了类似令人发指的东西)。

答案 2 :(得分:6)

根据cdecl.org

  

将foo声明为函数,返回指向数组SIZE的数组SIZE的指针   指向函数的指针返回浮点数指针

如果你想手动解码,请使用Luchian Grigore给出的螺旋规则。

答案 3 :(得分:4)

这里最好的做法是转换为一系列typedef。

typedef float * fnReturningPointerToFloat();
typedef fnReturningPointerToFloat* fnArray[SIZE][SIZE];
fnArray* foo();

答案 4 :(得分:3)

通常情况下,您可以尝试cdecl.org,但需要替换SIZE

假设您将SIZE交换为12,您将获得:

  

声明foo作为函数返回指向数组12的数组12的指针   指向函数的指针返回指向float的指针

我不确定这对你有什么帮助!

这里有两点意见:

  1. 我猜这个代码旁边没有评论,解释它的目的是什么(即不是技术解释它是什么,而是从功能/业务角度实现的)如果一个程序员需要使用像这样复杂的东西,它们应该足以向未来的维护者解释它的用途。
  2. 当然,在C ++中,有更明显且可能更安全的方法来实现同样的目标。

答案 5 :(得分:2)

本文件为我提供了关于如何轻松准备任何C声明的最佳线索:

http://c-faq.com/decl/spiral.anderson.html

  

有三个简单的步骤:

     
      
  • 从未知元素开始,以螺旋/顺时针方向移动;在考虑以下元素时,用相应的英语陈述替换它们:

         
        
    • [X][] =&gt;数组X大小为......或数组未定义大小...

    •   
    • (type1, type2) =&gt;函数传递type1和type2返回...

    •   
    • * =&gt;指针指向...

    •   
  •   
  • 继续以螺旋/顺时针方向执行此操作,直到所有令牌都被覆盖。

  •   
  • 始终先解决括号内的任何内容!

  •   

示例:

             +-------+
             | +-+   |
             | ^ |   |
        char *str[10];
         ^   ^   |   |
         |   +---+   |
         +-----------+

Question we ask ourselves: What is str?

``str is an...

- We move in a spiral clockwise direction starting with `str' and the first character we see is a `[' so, that means we have an array, so...
  ``str is an array 10 of...

- Continue in a spiral clockwise direction, and the next thing we encounter is the `*' so, that means we have pointers, so...
  ``str is an array 10 of pointers to...

- Continue in a spiral direction and we see the end of the line (the `;'), so keep going and we get to the type `char', so...
``str is an array 10 of pointers to char''

We have now ``visited'' every token; therefore we are done!

答案 6 :(得分:2)

尽管上面的大多数答案都足够好,但是仍然缺少用于解码复杂C声明的完整规则集。我在下面提供了一套完整的程序来解码任何复杂的C声明。这套规则实际上是基于运算符的优先级。可以将诸如右手螺旋规则之类的规则视为这些规则集的捷径。

在进行其他任何操作之前,我们需要了解一些内容以对声明进行解码。

声明的“基本类型”

C声明始终只有一种基本声明类型。这是声明的最左侧。 例如-

  • int a-基本类型为'int'
  • float *p-基本类型为“ float”
  • char (*p)[3]-基本类型为'char'

优先级和关联性

接下来,我们需要知道()[]*的优先顺序-解引用运算符

  1. ()[]-关联性从左到右
  2. *-关联性是从右到左

与上面每个运算符相对应的短语

接下来,我们需要知道与每个运算符相对应的解码短语。前面的示例将阐明这一点。

  • ()-函数返回
  • [SIZE]-SIZE数组
  • *-指向
  • 的指针

现在按照以下规则解码声明

  1. 总是先写变量名,后跟一个'is'。

    例如-

  • int a- a是 ...
  • float *p- p是 ...
  • char (*p)[3]- p是 ...
  1. 总是以基本类型结尾

    例如-

  • int a- a是 ... int
  • float *p- p是 ... float
  • char (*p)[3]- p是 ... char
  1. 现在使用以下子步骤填充介于两者之间的部分

    • 从名称开始,按照运算符的优先级和关联性选择下一个优先级最高的运算符,并将与之对应的短语附加到解码字符串的中间部分。

    • 对剩余的声明重复上述子步骤,直到解码过程完成

注1:为简单起见,我忽略了该函数的参数,但是可以将其包含在与()对应的短语之后。

注意2:括号(())会更改运算符的优先级顺序,就像在任何算术表达式中一样。

注意3::您可以在解码的声明中使用括号以提高可读性(我在下面的一些示例中已完成此操作)。将每组()视为一个单元。

注意4::n维数组实际上是...(n-1次)数组的个数组。对于ex-int A [2] [3]-A是2的数组(3 int的数组),即A是2个元素的数组,其中每个元素是包含3个整数的数组

示例

  • int a- a是int
  • float *p- p是指向浮点的指针
  • char (*p)[3]-p是指向3个字符的数组的指针

一些复杂的声明示例

  1. int **p[10]-p是10个指向int指针的指针的数组
  2. int (*p)[10]-p是指向10个int数组的指针
  3. int *p(char *a)-p是函数返回指向int的指针
  4. int (*p(char*a))[10]-p是函数返回(指向(10个整数的数组的指针))
  5. int *(*p)()-p是指向(函数返回(指向int的指针))的指针
  6. int (*p()[20])[10]-p是要返回的函数(20的数组(指向(10的int数组)的指针))

这组规则也可以与const一起使用-const限定符修改其左侧的术语(如果存在),否则修改其右侧的术语。

  1. const int *p[10]-p是指向int const的10个指针的数组
  2. int const *p[10]-p是指向const int的10个指针的数组(与第7个示例相同)
  3. int *const p[10]-p是指向int的10个const指针的数组

现在是一个非常复杂的示例,在实践中不会在任何地方使用它,但是仍然可以用来演示解码过程

  1. char *(*(**foo[][8])())[]-foo是(8的数组(指向(函数返回的指针(指向(char的指针的数组))的指针)))))的数组

现在最后解码问题中给出的声明

float * (*(*foo())[SIZE][SIZE])()-foo是函数返回(指向(SIZE数组(SIZE数组(指向(函数返回指向浮点的函数的)指针))的指针)))

以下是我从中阅读此解码过程的文章的链接

示例10摘自本文

http://www.unixwiz.net/techtips/reading-cdecl.html

答案 7 :(得分:1)

来自http://cdecl.org/

声明foo为函数返回指向数组SIZE的数组SIZE指向函数返回浮点数指针