我只是在互联网上浏览了一些代码,发现了这个:
float * (*(*foo())[SIZE][SIZE])()
我如何阅读这份声明?是否有一套特定的规则来阅读如此复杂的声明?
我只是在互联网上浏览了一些代码,发现了这个:
float * (*(*foo())[SIZE][SIZE])()
我如何阅读这份声明?是否有一套特定的规则来阅读如此复杂的声明?
我有一段时间没有这样做了!
开始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();
标准规则:找到最左边的标识符并找出路,记住这一点[]
并()
在之前绑定*
:
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;
}
这是我能想到做这样的事情的唯一原因。你不应该经常在野外看到这样的类型(尽管它们偶尔会出现,而且我写过类似令人发指的东西而感到内疚)。
这里最好的做法是转换为一系列 typedef。
typedef float * fnReturningPointerToFloat();
typedef fnReturningPointerToFloat* fnArray[SIZE][SIZE];
fnArray* foo();
通常,您可以尝试cdecl.org但您需要替换为SIZE
假设你换成SIZE
12,你会得到:
将 foo 声明为函数返回指向数组 12 的指针的函数的指针的数组 12 的指针返回指向浮点的指针
我不确定这真的对你有帮助!
这里有两个观察:
该文档为我提供了有关如何轻松准备任何 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!
尽管上面的大多数答案都足够好,但缺乏一套完整的规则来解码复杂的 C 声明。我在下面提供了一个完整的集合来解码任何复杂的 C 声明。这组规则实际上是基于运算符的优先级。诸如右手螺旋规则之类的规则可以被认为是这些规则集的捷径。
首先,我们需要知道一些事情来解码声明。
声明的“基本类型”
AC 声明始终只有一种基本声明类型。这是声明的最左侧位置。例如 -
int a
- 基本类型是'int'float *p
- 基本类型是“浮动”char (*p)[3]
- 基本类型是'char'优先级和关联性
接下来我们需要知道()
,[]
和*
- 解引用运算符的优先顺序
()
, []
- 关联性从左到右*
- 关联性是从右到左与上述每个运算符对应的短语
接下来我们需要知道每个算子对应的解码短语。前面的例子将清楚地说明这一点。
()
- 函数返回[SIZE]
- 大小数组*
- 指向的指针现在按照下面的规则来解码声明
总是先写变量名,然后是“is”。
例如 -
int a
-一个是...float *p
- p 是...char (*p)[3]
- p 是...总是以基本类型结尾
例如 -
int a
- a 是... intfloat *p
- p 是...浮动char (*p)[3]
- p 是...字符现在使用以下子步骤填写中间部分
从名称开始,按照运算符优先级和关联性选择下一个最高优先级运算符,并将与其对应的短语附加到解码字符串的中间部分。
对剩余的声明重复上述子步骤,直到解码过程完成
注意 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 个字符数组的指针一些复杂的声明示例
int **p[10]
- p 是 10 个指向 int 的指针的数组int (*p)[10]
- p 是指向 10 int 数组的指针int *p(char *a)
-p 是函数返回指向 int 的指针int (*p(char*a))[10]
- p 是函数返回(指向(10 int 数组)的指针)int *(*p)()
- p 是指向(函数返回(指向 int 的指针))的指针int (*p()[20])[10]
- p 是函数返回(20 的数组(指向(10 int 的数组)的指针))这组规则也可以与它一起使用const
- const 限定符修改它左侧的术语(如果存在),否则它修改它右侧的术语。
const int *p[10]
- p 是 10 个指向 int const 的指针的数组int const *p[10]
- p 是 10 个指向 const int 的指针的数组(这与第 7 个示例相同)int *const p[10]
- p 是 10 个指向 int 的 const 指针的数组现在是一个非常复杂的示例,它在实践中不会在任何地方使用,但仍可用于演示解码过程
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 摘自本文
将 foo 声明为函数返回指向数组的指针 指向函数的指针的数组 SIZE 返回指向浮点的指针