如何定义指向函数的指针 (如何定义指向指针的指针)
本文目录导航:
如何定义指向函数的指针?
一、定义一个函数指针的方法如下:
前往值类型 (* 指针变量名)([形参列表]);
1、int func(int x); /* 申明一个函数 */
2、int (*f) (int x); /* 申明一个函数指针 */
3、f=func; /* 将func函数的首地址赋给指针f */
二、函数指针的用法:
函数指针的运行:(*指针变量名)(参数列表)
如:int c=(*ptr)(a,b);/* 函数指针 的经常使用*/
在C言语中,函数自身不是变量,然而可以定义指向函数的指针,也称作函数指针,函数指针指向函数的入口地址。
这种类型的指针可以被赋值、寄存在数组中、传递给函数以及作为函数的前往值等等。
裁减资料:
指针函数和函数指针的区别:
1、这两个概念都是简称,指针函数是指前往值是指针的函数,即实质是一个函数。
咱们知道函数都有前往类型(假设不前往值,则为无值型),只不过指针函数前往类型是某一类型的指针。
2、前往指针的函数的用途十分宽泛。
理想上,每一个函数,即使它不带有前往某种类型的指针,它自身都有一个入口地址,该地址相当于一个指针。
比如函数前往一个整型值,实践上也相当于前往一个指针变量的值,不过这时的变量是函数自身而已,而整个函数相当于一个“变量”。
函数指针和指针函数用法和区别
在C言语学习中,函数指针和指针函数是常被提及的概念,尤其对初学者来说,了解它们的用法和区别是一项应战。
实践上,复杂的相关概念如函数指针函数、指针数组等对实践编程或许并不经常出现,因此,咱们先从基础的两个概念入手,它们在名目中更为适用。
指针函数
指针函数的基本定义是前往指针的函数。
申明时,方式为*类型标识符 函数名(参数表),例如,一个前往整型指针的函数申明可以写为:int* fun(int x, int y)。
这个函数与普通函数的不同在于,它的前往值是一个地址,而非数值。
在经常使用时,须要一个同类型的指针来接纳前往值。
函数指针
函数指针则是指针变量,它指向一个函数。
申明为类型说明符 (*函数名)(参数),如int (*fun)(int x, int y),区别在于这里的星号是在类型前面,代表它是一个指向函数的指针。
函数指针须要明白地给它赋一个函数的地址,调用方式有两种,明白经过指针调用更介绍。
区别总结
关键区别在于定义:指针函数是函数自身能前往指针,而函数指针是寄存函数地址的指针。
写法上,指针函数的星号在类型前面,函数指针的星号在函数名前。
便捷的区分方法是:带括号的函数名是函数指针,反之则是指针函数。
在实践运行中,了解这两个概念的经常使用方法至关关键,防止混杂。
经过详细示例,可以更好地把握它们的用法。
急。。请问一下,指向函数的指针有什么不凡的作用吗?为什么要有指向函数的指针呢?
指针是一个不凡的变量,它外面存储的数值被解释成为内存里的一个地址。
要搞清一个指针须要搞清指针的四方面的内容:指针的类型,指针所指向的 类型,指针的值或许叫指针所指向的内存区,还有指针自身所占据的内存区。
让咱们区分说明。
先申明几个指针放着做例子: 例一: (1)int*ptr; (2)char*ptr; (3)int**ptr; (4)int(*ptr)[3]; (5)int*(*ptr)[4]; 假设看疑问后几个例子的话,请参阅我前段期间贴出的文章<<如何了解c和c ++的复杂类型申明>>。
指针的类型 从语法的角度看,你只需把指针申明语句里的指针名字去掉,剩下的局部就是这个指针的类型。
这是指针自身所具备的类型。
让咱们看看例一中各个指针的类型: (1)int*ptr;//指针的类型是int* (2)char*ptr;//指针的类型是char* (3)int**ptr;//指针的类型是int** (4)int(*ptr)[3];//指针的类型是int(*)[3] (5)int*(*ptr)[4];//指针的类型是int*(*)[4] 怎样样?找出指针的类型的方法是不是很便捷? 指针所指向的类型 当你经过指针来访问指针所指向的内存区时,指针所指向的类型选择了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须把指针申明语句中的指针名字和名字左边的指针申明符*去掉,剩下的就是指针所指向的类型。
例如: (1)int*ptr;//指针所指向的类型是int (2)char*ptr;//指针所指向的的类型是char (3)int**ptr;//指针所指向的的类型是int* (4)int(*ptr)[3];//指针所指向的的类型是int()[3] (5)int*(*ptr)[4];//指针所指向的的类型是int*()[4] 在指针的算术运算中,指针所指向的类型有很大的作用。
指针的类型(即指针自身的类型)和指针所指向的类型是两个概念。
当你对C越来越相熟时,你会发现,把与指针搅和在一同的类型这个概念分红指针的类型和指针所指向的类型两个概念,是知晓指针的关键点之一。
我看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一同了,所以看起书来前后矛盾,越看越懵懂。
指针的值,或许叫指针所指向的内存区或地址 指针的值是指针自身存储的数值,这个值将被编译器当作一个地址,而不是一个普通的数值。
在32位程序里,一切类型的指针的值都是一个32位整数,由于32位程序里内存地址全都是32位长。
指针所指向的内存区就是从指针的值所代表的那个内存地址开局,长度为si zeof(指针所指向的类型)的一片内存区。
以后,咱们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;咱们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
指针所指向的内存区和指针所指向的类型是两个齐全不同的概念。
在例一中,指针所指向的类型曾经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或许说是有意义的。
以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里? 指针自身所占据的内存区 指针自身占了多大的内存?你只需用函数sizeof(指针的类型)测一下就知道了。
在32位平台里,指针自身占据了4个字节的长度。
指针自身占据的内存这个概念在判别一个指针表白式能否是左值时很有用。
指针的算术运算 指针可以加上或减去一个整数。
指针的这种运算的意义和理论的数值的加减运算的意义是不一样的。
例如: 例二: 1、chara[20]; 2、int*ptr=a; ... ... 3、ptr++; 在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。
接上去的第3句中,指针ptr被加了1,编译器是这样解决的:它把指针ptr的值加上了sizeof(int),在32位程序中,是被加上了4。
由于地址是用字节做单位的,故ptr所指向的地址由原来的变量a的地址向洼地址方向参与了4个字节。
由于char类型的长度是一个字节,所以,原来ptr是指向数组a的第0号单元开局的四个字节,此时指向了数组a中从第4号单元开局的四个字节。
咱们可以用一个指针和一个循环来遍历一个数组,看例子: 例三: intarray[20]; int*ptr=array; ... //此处略去为整型数组赋值的代码。
... for(i=0;i<20;i++) { (*ptr)++; ptr++; } 这个例子将整型数组中各个单元的值加1。
由于每次循环都将指针ptr加1,所以每次循环都能访问数组的下一个单元。
再看例子: 例四: 1、chara[20]; 2、int*ptr=a; ... ... 3、ptr+=5; 在这个例子中,ptr被加上了5,编译器是这样解决的:将指针ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。
由于地址的单位是字节,故如今的ptr所指向的地址比起加5后的ptr所指向的地址来说,向洼地址方向移动了20个字节。
在这个例子中,没加5前的ptr指向数组a的第0号单元开局的四个字节,加5后,ptr曾经指向了数组a的合法范畴之外了。
只管这种状况在运行上会出疑问,但在语法上却是可以的。
这也表现出了指针的灵敏性。
假设上例中,ptr是被减去5,那么解决环节迥然不同,只不过ptr的值是被减去5乘sizeof(int),新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移动了20个字节。
总结一下,一个指针ptrold加上一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相反,ptrnew所指向的类型和ptrold所指向的类型也相反。
ptrnew的值将比ptrold的值参与了n乘sizeof(ptrold所指向的类型)个字节。
就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向洼地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。
一个指针ptrold减去一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相反,ptrnew所指向的类型和ptrold所指向的类型也相反。
ptrnew的值将比ptrold的值缩小了n乘sizeof(ptrold所指向的类型)个字节,就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向低地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。
运算符&和* 这里&是取地址运算符,*是...书上叫做直接运算符。
&a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址嘛,那就是a的地址。
*p的运算结果就不拘一格了。
总之*p的结果是p所指向的物品,这个物品有这些特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。
例五: inta=12; intb; int*p; int**ptr; p=&a; //&a的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a的地址。
*p=24; //*p的结果,在这里它的类型是int,它所占用的地址是p所指向的地址,显然,*p就是变量a。
ptr=&p; //&p的结果是个指针,该指针的类型是p的类型加个*,在这里是int **。
该指针所指向的类型是p的类型,这里是int*。
该指针所指向的地址就是指针p自己的地址。
*ptr=&b; //*ptr是个指针,&b的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以用&b来给*ptr赋值就是毫无疑问的了。
**ptr=34; //*ptr的结果是ptr所指向的物品,在这里是一个指针,对这个指针再做一次性*运算,结果就是一个int类型的变量。
指针表白式 一个表白式的最后结果假设是一个指针,那么这个表白式就叫指针表式。
上方是一些指针表白式的例子: 例六: inta,b; intarray[10]; int*pa; pa=&a;//&a是一个指针表白式。
int**ptr=&pa;//&pa也是一个指针表白式。
*ptr=&b;//*ptr和&b都是指针表白式。
pa=array; pa++;//这也是指针表白式。
例七: char*arr[20]; char**parr=arr;//假设把arr看作指针的话,arr也是指针表白式 char*str; str=*parr;//*parr是指针表白式 str=*(parr+1);//*(parr+1)是指针表白式 str=*(parr+2);//*(parr+2)是指针表白式 由于指针表白式的结果是一个指针,所以指针表白式也具备指针所具备的四个因素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。
好了,当一个指针表白式的结果指针曾经明白地具备了指针自身占据的内存的话,这个指针表白式就是一个左值,否则就不是一个左值。
在例七中,&a不是一个左值,由于它还没有占据明白的内存。
*ptr是一个左值,由于*ptr这个指针曾经占据了内存,其实*ptr就是指针pa,既然pa曾经在内存中有了自己的位置,那么*ptr当然也有了自己的位置。
数组和指针的相关 假设对申明数组的语句不太明白的话,请参阅我前段期间贴出的文章<<如何了解c和c++的复杂类型申明>>。
数组的数组名其实可以看作一个指针。
看下例: 例八: intarray[10]=,value; ... ... value=array[0];//也可写成:value=*array; value=array[3];//也可写成:value=*(array+3); value=array[4];//也可写成:value=*(array+4); 上例中,普通而言数组名array代表数组自身,类型是int[10],但假设把array看做指针的话,它指向数组的第0个单元,类型是int*,所指向的类型是数组单元的类型即int。
因此*array等于0就一点也不奇异了。
同理,array+3是一个指向数组第3个单元的指针,所以*(array+3)等于3。
其它依此类推。
例九: char*str[3]={ Hello,thisisasample!, Hi,goodmorning., Helloworld }; chars[80]; strcpy(s,str[0]);//也可写成strcpy(s,*str); strcpy(s,str[1]);//也可写成strcpy(s,*(str+1)); strcpy(s,str[2]);//也可写成strcpy(s,*(str+2)); 上例中,str是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。
把指针数组名str当作一个指针的话,它指向数组的第0号单元,它的类型是char**,它指向的类型是char*。
*str也是一个指针,它的类型是char*,它所指向的类型是char,它指向的地址是字符串Hello,thisisasample!的第一个字符的地址,即H的地址。
str+1也是一个指针,它指向数组的第1号单元,它的类型是char**,它指向的类型是char*。
*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向 Hi,goodmorning.的第一个字符H,等等。
上方总结一下数组的数组名的疑问。
申明了一个数组TYPEarray[n],则数组称号array就有了两重含意:第一,它代表整个数组,它的类型是TYPE[n];第二 ,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,该指针自己占有独自的内存区,留意它和数组第0号单元占据的内存区是不同的。
该指针的值是不能修正的,即相似array++的表白式是失误的。
在不同的表白式中数组名array可以表演不同的角色。
在表白式sizeof(array)中,数组名array代表数组自身,故这时sizeof函数测出的是整个数组的大小。
在表白式*array中,array表演的是指针,因此这个表白式的结果就是数组第0号单元的值。
sizeof(*array)测出的是数组单元的大小。
表白式array+n(其中n=0,1,2,....。
)中,array表演的是指针,故array+n的结果是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第n号单元。
故sizeof(array+n)测出的是指针类型的大小。
例十: intarray[10]; int(*ptr)[10]; ptr=&array; 上例中ptr是一个指针,它的类型是int(*)[10],他指向的类型是int[10] ,咱们用整个数组的首地址来初始化它。
在语句ptr=&array中,array代表数组自身。
本节中提到了函数sizeof(),那么我来问一问,sizeof(指针称号)测出的终究是指针自身类型的大小呢还是指针所指向的类型的大小?答案是前者。
例如: int(*ptr)[10]; 则在32位程序中,有: sizeof(int(*)[10])==4 sizeof(int[10])==40 sizeof(ptr)==4 实践上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。
指针和结构类型的相关 可以申明一个指向结构类型对象的指针。
例十一: structMyStruct { inta; intb; intc; } MyStructss=; //申明了却构对象ss,并把ss的三个成员初始化为20,30和40。
MyStruct*ptr=&ss; //申明了一个指向结构对象ss的指针。
它的类型是MyStruct*,它指向的类型是MyStruct。
int*pstr=(int*)&ss; //申明了一个指向结构对象ss的指针。
然而它的类型和它指向的类型和ptr是不同的。
请问怎样经过指针ptr来访问ss的三个成员变量? 答案: ptr->a; ptr->b; ptr->c; 又请问怎样经过指针pstr来访问ss的三个成员变量? 答案: *pstr;//访问了ss的成员a。
*(pstr+1);//访问了ss的成员b。
*(pstr+2)//访问了ss的成员c。
只管我在我的MSVC++6.0上调式过上述代码,然而要知道,这样经常使用pstr来访问结形成员是不正轨的,为了说明为什么不正轨,让咱们看看怎样经过指针来访问数组的各个单元: 例十二: intarray[3]=; int*pa=array; 经过指针pa访问数组array的三个单元的方法是: *pa;//访问了第0号单元 *(pa+1);//访问了第1号单元 *(pa+2);//访问了第2号单元 从格局上看倒是与经过指针访问结形成员的不正轨方法的格局一样。
一切的C/C++编译器在陈列数组的单元时,总是把各个数组单元寄存在延续的存储区里,单元和单元之间没有空隙。
但在寄存结构对象的各个成员时,在某种编译环境下,或许会须要字对齐或双字对齐或许是别的什么对齐,须要在相邻两个成员之间加若干个填充字节,这就造成各个成员之间或许会有若干个字节的空隙。
所以,在例十二中,即使*pstr访问到了却构对象ss的第一个成员变量a,也不能保障*(pstr+1)就必定能访问到结形成员b。
由于成员a和成员b之间或许会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。
这也证实了指针的灵敏性。
要是你的目标就是想看看各个结形成员之间究竟有没有填充字节,嘿,这倒是个不错的方法。
经过指针访问结形成员的正确方法应该是象例十二中经常使用指针ptr的方法。
指针和函数的相关 可以把一个指针申明成为一个指向函数的指针。
intfun1(char*,int); int(*pfun1)(char*,int); pfun1=fun1; .... .... inta=(*pfun1)(abcdefg,7);//经过函数指针调用函数。
可以把指针作为函数的形参。
在函数调用语句中,可以用指针表白式来作为实参。
例十三: intfun(char*); inta; charstr[]=abcdefghijklmn; a=fun(str); ... ... intfun(char*s) { intnum=0; for(inti=0;i{ num+=*s;s++; } returnnum; } 这个例子中的函数fun统计一个字符串中各个字符的ASCII码值之和。
前面说了,数组的名字也是一个指针。
在函数调用中,当把str作为实参传递给形参s后,实践是把str的值传递给了s,s所指向的地址就和str所指向的地址分歧,然而str和s各自占用各自的存储空间。
在函数体内对s启动自加1运算,并不象征着同时对str启动了自加1运算。
指针类型转换 当咱们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的左边是一个指针表白式。
在咱们前面所举的例子中,绝大少数状况下,指针的类型和指针表白式的类型是一样的,指针所指向的类型和指针表白式所指向的类型是一样的。
例十四: 1、floatf=12.3; 2、float*fptr=&f; 3、int*p; 在上方的例子中,假设咱们想让指针p指向实数f,应该怎样搞?是用上方的语句吗? p=&f; 不对。
由于指针p的类型是int*,它指向的类型是int。
表白式&f的结果是一个指针,指针的类型是float*,它指向的类型是float。
两者不分歧,直接赋值的方法是不行的。
至少在我的MSVC++6.0上,对指针的赋值语句要求赋值号两头的类型分歧,所指向的类型也分歧,其它的编译器上我没试过,大家可以试试。
为了成功咱们的目标,须要启动强迫类型转换: p=(int*)&f; 假设有一个指针p,咱们须要把它的类型和所指向的类型改为TYEP*TYPE, 那么语法格局是: (TYPE*)p; 这样强迫类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。
而原来的指针p的一切属性都没有被修正。
一个函数假设经常使用了指针作为形参,那么在函数调用语句的实参和形参的联合环节中,也会出现指针类型的转换。
例十五: voidfun(char*); inta=125,b; fun((char*)&a); ... ... voidfun(char*s) { charc; c=*(s+3);*(s+3)=*(s+0);*(s+0)=c; c=*(s+2);*(s+2)=*(s+1);*(s+1)=c; } } 留意这是一个32位程序,故int类型占了四个字节,char类型占一个字节。
函数fun的作用是把一个整数的四个字节的顺序来个颠倒。
留意到了吗?在函数调用语句中,实参&a的结果是一个指针,它的类型是int*,它指向的类型是int。
形参这个指针的类型是char*,它指向的类型是char。
这样,在实参和形参的联合环节中,咱们必定启动一次性从int*类型到char*类型的转换。
联合这个例子,咱们可以这样来构想编译器启动转换的环节:编译器先结构一个暂时指针char*temp, 而后口头temp=(char*)&a,最后再把temp的值传递给s。
所以最后的结果是:s的类型是char*,它指向的类型是char,它指向的地址就是a的首地址。
咱们曾经知道,指针的值就是指针指向的地址,在32位程序中,指针的值其实是一个32位整数。
那可无法以把一个整数当作指针的值直接赋给指针呢?就象上方的语句: unsignedinta; TYPE*ptr;//TYPE是int,char或结构类型等等类型。
... ... a=; ptr=;//咱们的目标是要使指针ptr指向地址(十进制 ) ptr=a;//咱们的目标是要使指针ptr指向地址(十进制) 编译一下吧。
结果发现前面两条语句全是错的。
那么咱们的目标就不能到达了吗?不,还有方法: unsignedinta; TYPE*ptr;//TYPE是int,char或结构类型等等类型。
... ... a=某个数,这个数必定代表一个合法的地址; ptr=(TYPE*)a;//呵呵,这就可以了。
严厉说来这里的(TYPE*)和指针类型转换中的(TYPE*)还不一样。
这里的(TYPE*)的意思是把无符号整数a的值当作一个地址来看待。
上方强调了a的值必定代表一个合法的地址,否则的话,在你经常使用ptr的时刻,就会出现合法操作失误。
想想能不能反上来,把指针指向的地址即指针的值当作一个整数取进去。
完 全可以。
上方的例子展示了把一个指针的值当作一个整数取进去,而后再把这个整数当作一个地址赋给一个指针: 例十六: inta=123,b; int*ptr=&a; char*str; b=(int)ptr;//把指针ptr的值当作一个整数取进去。
str=(char*)b;//把这个整数的值当作一个地址赋给指针str。
如今咱们曾经知道了,可以把指针的值当作一个整数取进去,也可以把一个整数值当作地址赋给一个指针。
指针的安保疑问 看上方的例子: 例十七: chars=a; int*ptr; ptr=(int*)&s; *ptr=1298; 指针ptr是一个int*类型的指针,它指向的类型是int。
它指向的地址就是s的首地址。
在32位程序中,s占一个字节,int类型占四个字节。
最后一条语句岂但扭转了s所占的一个字节,还把和s相临的洼地址方向的三个字节也扭转了。
这三个字节是干什么的?只要编译程序知道,而写程序的人是不太或许知道的。
兴许这三个字节里存储了十分关键的数据,兴许这三个字节里正好是程序的一条代码,而由于你对指针的马虎运行,这三个字节的值被扭转了!这会形成解体性的失误。
让咱们再来看一例: 例十八: 1、chara; 2、int*ptr=&a; ... ... 3、ptr++; 4、*ptr=115; 该例子齐全可以经过编译,并能口头。
然而看到没有?第3句对指针ptr启动自加1运算后,ptr指向了和整形变量a相邻的洼地址方向的一块存储区。
这块存储区里是什么?咱们不知道。
有或许它是一个十分关键的数据,甚至或许是一条代码。
而第4句居然往这片存储区里写入一个数据!这是重大的失误。
所以在经常使用指针时,程序员心里必定十分分明:我的指针终究指向了哪里。
在用指针访问数组的时刻,也要留意不要超出数组的低端和上流界限,否则也会形成相似的失误。
在指针的强迫类型转换:ptr1=(TYPE*)ptr2中,假设sizeof(ptr2的类型)大于sizeof(ptr1的类型),那么在经常使用指针ptr1来访问ptr2所指向的存储区时是安保的。
假设sizeof(ptr2的类型)小于sizeof(ptr1的类型),那么在经常使用指针ptr1来访问ptr2所指向的存储区时是不安保的。
©2008 Baidu