我刚刚在互联网上查看了一些代码,发现了这个:
float * (*(*foo())[SIZE][SIZE])()
我如何阅读此声明?是否有一套特定的规则来阅读这些复杂的声明?
答案 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)
答案 3 :(得分:4)
这里最好的做法是转换为一系列typedef。
typedef float * fnReturningPointerToFloat();
typedef fnReturningPointerToFloat* fnArray[SIZE][SIZE];
fnArray* foo();
答案 4 :(得分:3)
通常情况下,您可以尝试cdecl.org,但需要替换SIZE
假设您将SIZE
交换为12,您将获得:
声明foo作为函数返回指向数组12的数组12的指针 指向函数的指针返回指向float的指针
我不确定这对你有什么帮助!
这里有两点意见:
答案 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'优先级和关联性
接下来,我们需要知道()
,[]
和*
的优先顺序-解引用运算符
()
,[]
-关联性从左到右*
-关联性是从右到左与上面每个运算符相对应的短语
接下来,我们需要知道与每个运算符相对应的解码短语。前面的示例将阐明这一点。
()
-函数返回[SIZE]
-SIZE数组*
-指向现在按照以下规则解码声明
总是先写变量名,后跟一个'is'。
例如-
int a
- a是 ... float *p
- p是 ... char (*p)[3]
- p是 ... 总是以基本类型结尾
例如-
int a
- a是 ... int float *p
- p是 ... float char (*p)[3]
- p是 ... char 现在使用以下子步骤填充介于两者之间的部分
从名称开始,按照运算符的优先级和关联性选择下一个优先级最高的运算符,并将与之对应的短语附加到解码字符串的中间部分。
对剩余的声明重复上述子步骤,直到解码过程完成
注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个字符的数组的指针一些复杂的声明示例
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 *(*p)()
-p是指向(函数返回(指向int的指针))的指针int (*p()[20])[10]
-p是要返回的函数(20的数组(指向(10的int数组)的指针))这组规则也可以与const
一起使用-const限定符修改其左侧的术语(如果存在),否则修改其右侧的术语。
const int *p[10]
-p是指向int const的10个指针的数组int const *p[10]
-p是指向const int的10个指针的数组(与第7个示例相同)int *const p[10]
-p是指向int的10个const指针的数组现在是一个非常复杂的示例,在实践中不会在任何地方使用它,但是仍然可以用来演示解码过程
char *(*(**foo[][8])())[]
-foo是(8的数组(指向(函数返回的指针(指向(char的指针的数组))的指针)))))的数组现在最后解码问题中给出的声明
float * (*(*foo())[SIZE][SIZE])()
-foo是函数返回(指向(SIZE数组(SIZE数组(指向(函数返回指向浮点的函数的)指针))的指针)))
以下是我从中阅读此解码过程的文章的链接
示例10摘自本文
答案 7 :(得分:1)
声明foo为函数返回指向数组SIZE的数组SIZE指向函数返回浮点数指针