70

我只是在互联网上浏览了一些代码,发现了这个:

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

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

4

8 回答 8

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 数组的指针,返回一个指向浮点数的指针


不管是谁写的,请教他使用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();
于 2013-02-27T12:20:31.630 回答
100

标准规则:找到最左边的标识符并找出路,记住这一点[]()在之前绑定*

            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;
}

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

于 2013-02-27T12:31:15.390 回答
6

根据cdecl.org

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

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

于 2013-02-27T12:01:32.383 回答
4

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

typedef float * fnReturningPointerToFloat();
typedef fnReturningPointerToFloat* fnArray[SIZE][SIZE];
fnArray* foo();
于 2013-02-27T12:21:42.977 回答
3

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

假设你换成SIZE12,你会得到:

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

我不确定这真的对你有帮助!

这里有两个观察:

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

该文档为我提供了有关如何轻松准备任何 C 声明的最佳线索:

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

需要遵循三个简单的步骤:

  • 从未知元素开始,沿螺旋/顺时针方向移动;当遇到以下元素时,用相应的英文语句替换它们:

    • [X]or [] => Array X size of ... 或 Array undefined size of ...

    • (type1, type2)=> 函数传递 type1 和 type2 返回 ...

    • *=> 指向...的指针

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

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

例子 :

             +-------+
             | +-+   |
             | ^ |   |
        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!
于 2013-03-05T19:59:13.743 回答
2

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

首先,我们需要知道一些事情来解码声明。

声明的“基本类型”

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

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

优先级和关联性

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

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

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

接下来我们需要知道每个算子对应的解码短语。前面的例子将清楚地说明这一点。

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

现在按照下面的规则来解码声明

  1. 总是先写变量名,然后是“is”。

    例如 -

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

    例如 -

  • int a- a 是... int
  • float *p- p 是...浮动
  • char (*p)[3]- p 是...字符
  1. 现在使用以下子步骤填写中间部分

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

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

注意 1: 为简单起见,我忽略了函数的参数,但它可以包含在对应于 . 的短语之后()

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

注意 3: 您可以在解码后的声明中使用括号来增加可读性(我在下面的一些示例中已经这样做了)。将每组 () 视为一个单元。

注 4: 一个 n 维数组实际上是一个数组的数组 ... (n-1 times) array。例如 - int A[2][3] - A 是 2 数组(3 int 数组),即 A 是 2 个元素的数组,其中每个元素都是包含 3 个整数的数组

例子

  • int a- a 是整数
  • 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 int 数组)的指针)
  5. int *(*p)()- p 是指向(函数返回(指向 int 的指针))的指针
  6. int (*p()[20])[10]- p 是函数返回(20 的数组(指向(10 int 的数组)的指针))

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

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

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

  1. char *(*(**foo[][8])())[]- foo 是数组 (array of 8 (pointer to (pointer to (function returned (pointer to (array of (pointer to char)))))))

现在终于解码问题中给出的声明

float * (*(*foo())[SIZE][SIZE])()- foo 是函数返回 (pointer to (array of SIZE (array of SIZE (pointer to (function return pointer to float)))))

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

示例 10 摘自本文

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

于 2020-10-29T14:23:05.113 回答
1

来自http://cdecl.org/

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

于 2013-02-27T12:01:55.467 回答