如何理解下面复杂的声明?
char (*(*f())[])();
char (*(*X[3])())[5];
void (*f)(int,void (*)());
char far *far *ptr;
typedef void (*pfun)(int,float);
int **(*f)(int**,int**(*)(int **,int **));
如何理解下面复杂的声明?
char (*(*f())[])();
char (*(*X[3])())[5];
void (*f)(int,void (*)());
char far *far *ptr;
typedef void (*pfun)(int,float);
int **(*f)(int**,int**(*)(int **,int **));
正如其他人所指出的那样,CDECL是这份工作的合适工具。
如果您想了解那种声明,而不从CDECL的帮助下,尝试从一些内幕,并从右至左读
以从列表中选择一个随机例如char (*(*X[3])())[5];
开始在X,这是所声明/定义的标识符(和最内层标识符):
char (*(*X[3])())[5];
^
X是
X[3]
^^^
3的阵列
(*X[3])
^ /* the parenthesis group the sub-expression */
指针 ,以
(*X[3])()
^^
接受的参数未指定的(但固定的)数
(*(*X[3])())
^ /* more grouping parenthesis */
和返回指针
(*(*X[3])())[5]
^^^
到5的阵列
char (*(*X[3])())[5];
^^^^ ^
字符 。
由内而外,类似于你将如何求解方程如读出来{3+5*[2+3*(x+6*2)]}=0
-你会通过解决启动里面有什么()
然后[]
最后{}
char (*(*x())[])()
^
这意味着, x
是后话 。
char (*(*x())[])()
^^
x
是一个函数 。
char (*(*x())[])()
^
x
返回一个指针的东西 。
char (*(*x())[])()
^ ^^^
x
返回一个指针数组 。
char (*(*x())[])()
^
x
返回一个指向指针数组 。
char (*(*x())[])()
^ ^^^
x
返回一个指向指针数组以功能
char (*(*x())[])()
^^^^
含义由返回的数组的指针x
点的函数数组的指针即指向返回一个字符的功能。
但是,是的,使用的cdecl 。 我用它自己来检查我的答案:)。
如果这仍扑朔迷离你(它可能应该),尝试做在一张纸上或你喜欢的文本编辑器同样的事情。 有没有知道什么它只是看它的意思的方式。
听起来像是为CDECL工具作业:
cdecl> explain char (*(*f())[])();
declare f as function returning pointer to array of pointer to function returning char
我环顾四周,该工具的官方网站,但无法找到一个看起来很真诚。 在Linux中,你通常可以期望你的选择,包括工具的分布,所以我只是为了产生上述样品安装它。
您应该使用的cdecl工具。 它应该可以在大多数Linux发行版。
例如,对于这个功能,它会回报你:
char (*(*f())[])();
- 声明˚F作为函数返回指针指向函数返回炭的阵列
void (*f)(int,void (*)());
- 函数指针的F原型。 f是一个函数,它有两个参数,第一个为int,而第二个是用于返回空隙的功能的功能的指针。
char far *far *ptr;
- PTR是远指针到远指针(其指向一些字符/字节)。
char (*(*X[3])())[5];
- X是3个指针的数组起作用接受的参数的数目undeterminate和返回指针到5字符数组。
typedef void (*pfun)(int,float);
- 声明函数指针pfun。 pfun是fuctnion采用两个参数,第一个为int,第二个是浮动型的。 该函数没有返回值;
如
void f1(int a, float b)
{ //do something with these numbers
};
顺便说一句,复杂的声明作为最后一个是不是经常看到。 这里是我正好弥补了这一目的的例子。
int **(*f)(int**,int**(*)(int **,int **));
typedef int**(*fptr)(int **,int **);
int** f0(int **a0, int **a1)
{
printf("Complicated declarations and meaningless example!\n");
return a0;
}
int ** f1(int ** a2, fptr afptr)
{
return afptr(a2, 0);
}
int main()
{
int a3 = 5;
int * pa3 = &a3;
f = f1;
f(&pa3, f0);
return 0;
}
看来,你的实际问题是这样的:
什么是用例的指针的指针?
指针的指针倾向于显示,当你有一些类型T的数组,而T本身是一个指向别的东西。 例如,
char *
。 char *x[10]
x
是10个指针的数组,以char
,又名10串。 在这一点上,你可能想知道char **
用武之地它进入图片从指针算术和阵列之间C.数组名称,非常密切的关系x
是(几乎)总是被转换为一个指向它的第一元件。
char *
。 char **
。 在C中,阵列E1[E2]
被定义为等同于*(E1 + E2)
通常情况下, E1
是数组名,让我们说x
,它会自动转换为char **
,和E2
是一些指标,比如说3(这条规则也解释了为什么3[x]
和x[3]
是一样的。 )
指针的指针也显示,当你需要一些类型的动态分配的数组T
,这本身就是一个指针 。 首先,让我们假装我们不知道是什么类型T。
T *vec
。 T *
可以作为连续序列的碱基T
的在存储器中。 n
元素? vec = malloc(n * sizeof(T))
; 这个故事是真实的绝对任何类型的T
,所以它是真正char *
。
vec
如果T
是char *
? char **vec
。 指针的指针也显示,当你有需要修改类型T,本身就是一个指针参数的函数 。
strtol
: long strtol(char *s, char **endp, int b)
strtol
从基部的字符串转换b
为整数。 它想告诉你多远它得到的字符串。 这或许可以返回同时含有一个struct long
和char *
,但是这不是多么声明。 char *
。 char **
。 如果你漫步在这条道路足够长的时间,你也可以运行到T ***
类型,但你几乎总是可以重构代码以避免它们。
最后, 指针的指针出现在链表的某些棘手的实现 。 考虑C.一个双向链表的申报标准
struct node {
struct node *next;
struct node *prev;
/* ... */
} *head;
这工作得很好,但我不会在这里重现插入/删除功能,但它有一个小问题。 任何节点都可以从列表中移除(或收到插入一个新的节点),而不引用列表的头部。 嗯,不太任何节点。 这不是列表,其中的第一个元素的真正prev
将为空。 这可能是在某些类型的C代码,你更与节点比列表作为一个概念本身工作适度讨厌。 这是在低级别的系统代码相当普遍的现象。
如果我们改写node
是这样的:
struct node {
struct node *next;
struct node **prevp;
/* ... */
} *head;
在每一个节点, prevp
点不是在一个节点,但在前面的节点的next
指针。 怎么样的第一个节点? 这是prevp
点, head
。 如果你画了像这样的列表(你必须把它画出来,了解如何工作的),你会看到,你可以删除第一个元素或插入第一个元素之前一个新的节点,而无需显式引用head
的名字。
X:函数返回指针数组指针的[]将函数返回字符” - 哈?
你有一个函数
该函数返回一个指针。
该指针指向数组。
该阵列是一个函数指针阵列(或指向函数的指针)
这些函数返回的char *。
what's the use case for a pointer to a pointer?
一种是通过参数,以方便返回值。
比方说,你有
int function(int *p)
*p = 123;
return 0; //success !
}
你这样称呼它
int x;
function(&x);
正如你所看到的,对于function
能够修改我们的x
,我们有一个指针把它传递给我们的X。
如果x
不是一个整数,而是一个char *
? 那么,它仍然是相同的,我们有一个指针传递给。 的指针的指针:
int function(char **p)
*p = "Hello";
return 0; //success !
}
你这样称呼它
char *x;
function(&x);
Remo.D的答案阅读功能,是一个很好的建议。 这里有一些答案给了别人。
一个用例为指针的指针是当你想将它传递给将修改指针的函数。 例如:
void foo(char **str, int len)
{
*str = malloc(len);
}
此外,这可能是一个字符串数组:
void bar(char **strarray, int num)
{
int i;
for (i = 0; i < num; i++)
printf("%s\n", strarray[i]);
}
通常情况下, 不应该使用这个声明复杂的,虽然有时候你确实需要是对于像函数指针非常复杂的类型。 在这种情况下,它更可读使用中间类型的类型定义; 例如:
typedef void foofun(char**, int);
foofun *foofunptr;
或者,你的“函数返回指向数组指针的[]以函数返回字符”第一个例子,你可以这样做:
typedef char fun_returning_char();
typedef fun_returning_char *ptr_to_fun;
typedef ptr_to_fun array_of_ptrs_to_fun[];
typedef array_of_ptrs_to_fun *ptr_to_array;
ptr_to_array myfun() { /*...*/ }
在实践中,如果你正在写什么明智的,很多的这些东西都会有自己的一个有意义的名字; 举例来说,这些可能是回国(的某种)名称的功能,所以fun_returning_char
可能是name_generator_type
和array_of_ptrs_to_fun
可能是name_generator_list
。 所以,你可以折叠它几行,并只定义这两个类型定义 - 这很可能会是有用的,任何情况下,在其他地方。
char far *far *ptr;
这是一个过时的微软形式,可以追溯到MS-DOS和非常早期的Windows天。 简短的版本是,这是一个远指针远指针为char,其中远指针可以在内存中指向任何地方,而不是近指针,它只能在64K数据段指向任何地方。 你真的不希望了解微软内存模型细节周围的完全脑死亡英特尔80x86的工作分段存储器架构。
typedef void (*pfun)(int,float);
这声明pfun作为一个指向接受一个int和浮子的过程的类型定义。 你通常会在函数声明或原型,即使用。
float foo_meister(pfun rabbitfun)
{
rabbitfun(69, 2.47);
}
我们必须给所有指针声明语句评估从左至右,从那里指针名或声明的名字在声明中宣布开始。
在评估的声明,我们必须从最里面的括号开始。
先从指针名字或函数的名称,然后在parenthersis最右边的字符,然后由最左边的字符follwed。
例:
char (*(*f())[])();
^
char (*(*f())[])();
^^^
In here f is a function name, so we have to start from that.
F()
char (*(*f())[])();
^
Here there are no declarations on the righthand side of the current
parenthesis, we do have to move to the lefthand side and take *:
char (*(*f())[])();
^
f() *
我们已经完成了内部括号字符,现在我们要回到这背后一个级别:
char (*(*f())[])();
------
现在采取[],因为这是对当前括号的右侧。
char (*(*f())[])();
^^
f() * []
现在采取的*因为右侧没有字符。
char (*(*f())[])();
^
char (*(*f())[])();
^
f() * [] *
char (*(*f())[])();
下一步评估外部打开和关闭括号,它的指示功能。
f() * [] * ()
char (*(*f())[])();
现在,我们可以在声明的末尾添加数据类型。
f() * [] * () char.
char (*(*f())[])();
最终的答案:
f() * [] * () char.
f是返回指针阵列的指针[]起作用返回炭的功能。
忘掉1和2 - 这仅仅是理论上的。
3:这是在程序入口函数使用int main(int argc, char** argv)
。 您可以通过使用访问字符串列表char**
。 的argv [0] =第一串,的argv [1] =第二串,...
指针传递作为参数的函数让该函数改变变量的内容指出,它可以是用于通过比函数的返回值其他方式返回信息是有用的。 例如,可能已被使用的返回值来指示错误/成功,或者你可能想返回多个值。 在调用代码本的语法是FOO(VAR),这需要VAR的地址,即,无功的指针。
因此,作为这样的,如果你想要的功能更改其内容的变量本身就是一个指针(例如,一个字符串),该参数将被声明为指针的指针 。
#include <stdio.h>
char *some_defined_string = "Hello, " ;
char *alloc_string() { return "World" ; } //pretend that it's dynamically allocated
int point_me_to_the_strings(char **str1, char **str2, char **str3)
{
*str1 = some_defined_string ;
*str2 = alloc_string() ;
*str3 = "!!" ;
if (str2 != 0) {
return 0 ; //successful
} else {
return -1 ; //error
}
}
main()
{
char *s1 ; //uninitialized
char *s2 ;
char *s3 ;
int success = point_me_to_the_strings(&s1, &s2, &s3) ;
printf("%s%s%s", s1, s2, s3) ;
}
需要注意的是main()中不分配的字符串的任何存储,所以point_me_to_the_strings(),因为它会如果他们被作为指针指向字符传递不写入STR1,STR2和STR3。 相反,point_me_to_the_strings()改变了自己的指针,使它们指向不同的地方,它可以做到这一点,因为它有指向他们。