
- 解 释
- 指向函数的指针变量
- 外文名
- Function pointer
- 行 业
- IT
- 中文名
- 函数指针
-
函数指针和指针函数用法和区别
2018-05-24 08:11:10函数指针和指针函数,在学习 C 语言的时候遇到这两个东西简直头疼,当然还有更头疼的,比如什么函数指针函数、指针函数指针、数组指针、指针数组、函数指针数组等等,描述越长其定义就越复杂,当然理解起来就越难,...前言
函数指针和指针函数,在学习 C 语言的时候遇到这两个东西简直头疼,当然还有更头疼的,比如什么函数指针函数、指针函数指针、数组指针、指针数组、函数指针数组等等,描述越长其定义就越复杂,当然理解起来就越难,特别是刚开始学习这门语言的童鞋,估计碰到这些东西就已经要崩溃了,然后好不容易死记硬背下来应付考试或者面试,然后过了几天发现,又是根本不会用,也不知道该在哪些地方用,这就尴尬了。
今天这里只讲两个相对简单的,其实上面说那些太复杂的东西也真的很少用,即便是用了理解起来很麻烦,所以莫不如先深刻理解这两个比较容易的,并且项目中比较常用到。正文
先来看看两者的定义以及说明。
指针函数
定义
指针函数,简单的来说,就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。
声明格式为:*类型标识符 函数名(参数表)这似乎并不难理解,再进一步描述一下。
看看下面这个函数声明:int fun(int x,int y);
这种函数应该都很熟悉,其实就是一个函数,然后返回值是一个 int 类型,是一个数值。
接着看下面这个函数声明:int *fun(int x,int y);
这和上面那个函数唯一的区别就是在函数名前面多了一个*号,而这个函数就是一个指针函数。其返回值是一个 int 类型的指针,是一个地址。
这样描述应该很容易理解了,所谓的指针函数也没什么特别的,和普通函数对比不过就是其返回了一个指针(即地址值)而已。
指针函数的写法
int *fun(int x,int y); int * fun(int x,int y); int* fun(int x,int y);
这个写法看个人习惯,其实如果*靠近返回值类型的话可能更容易理解其定义。
示例
(由于本人习惯于 Qt 中进行开发,所以这里为了方便,示例是在 Qt 工程中写的,其语法是一样的,只是输出方式不同)
来看一个非常简单的示例:typedef struct _Data{ int a; int b; }Data; //指针函数 Data* f(int a,int b){ Data * data = new Data; data->a = a; data->b = b; return data; } int main(int argc, char *argv[]) { QApplication a(argc, argv); //调用指针函数 Data * myData = f(4,5); qDebug() << "f(4,5) = " << myData->a << myData->b; return a.exec(); }
输出如下:
f(4,5) = 4 5
注意:在调用指针函数时,需要一个同类型的指针来接收其函数的返回值。
不过也可以将其返回值定义为 void*类型,在调用的时候强制转换返回值为自己想要的类型,如下://指针函数 void* f(int a,int b){ Data * data = new Data; data->a = a; data->b = b; return data; } 调用: Data * myData = static_cast<Data*>(f(4,5));
其输出结果是一样的,不过不建议这么使用,因为强制转换可能会带来风险。
函数指针
定义
函数指针,其本质是一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。
声明格式:类型说明符 (*函数名) (参数)
如下:int (*fun)(int x,int y);
函数指针是需要把一个函数的地址赋值给它,有两种写法:
fun = &Function; fun = Function;
取地址运算符&不是必需的,因为一个函数标识符就表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。
调用函数指针的方式也有两种:
x = (*fun)(); x = fun();
两种方式均可,其中第二种看上去和普通的函数调用没啥区别,如果可以的话,建议使用第一种,因为可以清楚的指明这是通过指针的方式来调用函数。当然,也要看个人习惯,如果理解其定义,随便怎么用都行啦。
示例
int add(int x,int y){ return x+y; } int sub(int x,int y){ return x-y; } //函数指针 int (*fun)(int x,int y); int main(int argc, char *argv[]) { QApplication a(argc, argv); //第一种写法 fun = add; qDebug() << "(*fun)(1,2) = " << (*fun)(1,2) ; //第二种写法 fun = ⊂ qDebug() << "(*fun)(5,3) = " << (*fun)(5,3) << fun(5,3); return a.exec(); }
输出如下:
(*fun)(1,2) = 3 (*fun)(5,2) = 2 2
上面说到的几种赋值和调用方式我都分别使用了,其输出结果是一样的。
二者区别
通过以上的介绍,应该都能清楚的理解其二者的定义。那么简单的总结下二者的区别:
定义不同
指针函数本质是一个函数,其返回值为指针。
函数指针本质是一个指针,其指向一个函数。写法不同
指针函数:int* fun(int x,int y);
函数指针:int (*fun)(int x,int y);
可以简单粗暴的理解为,指针函数的*是属于数据类型的,而函数指针的星号是属于函数名的。
再简单一点,可以这样辨别两者:函数名带括号的就是函数指针,否则就是指针函数。用法不同
上面已经写了详细示例,这里就不在啰嗦了。
总而言之,这两个东西很容易搞混淆,一定要深入理解其两者定义和区别,避免犯错。
另外,本文都是针对普通函数指针进行介绍,如果是C++非静态成员函数指针,其用法会有一些区别,在另外一篇博客中单独介绍,文章在这里
-
函数指针
2019-05-25 09:43:47什么是函数指针 如果程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址,即是地址我们就可以定义一个指针变量来...什么是函数指针
如果程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址,即是地址我们就可以定义一个指针变量来存放,这个指针变量就叫做函数指针变量,简称函数指针。
那么这个指针变量怎么定义呢?虽然同样是指向一个地址,但指向指针变量同我们之前讲过的指向变量的指针变量的定义的方式是不一样的,
int(*p)(int, int);
这个语句就定义了一个指向函数的指针变量 p。首先他是一个指针变量,所以要有一个"*",即(p),其次前面的 int 表示这个指针变量可以指向返回值类型,为 int 类型的函数。后面括号中的两个int表示这个指针变量可以指向有两个参数且都是int型的函数。所以和起来这个语句的意思就是:定义了一个指针变量p,该指针变量可以指向返回值类型为 int 型,且有两个整数参数的函数, p 的类型为 int()(int, int);
函数返回值类型 (*指针变量名)(函数参数列表);
如何用函数指针调用函数呢?
给大家举一个例子:
int Func(int x); int (*p) (int x); p = Func;
#include<stdio.h> #include<stdlib.h> int Max(int, int); int main(void) { int (*p) (int, int); int a, b, c; p = Max; printf("please enter a and b"); scanf("%d%d", &a, &b); c = (*p)(a, b); printf("a = %d\n b = %d\n max = %d\n", a, b, c); return 0; } int Max(int x, int y) { int z; if(x>y) { z = x; } else { z = y; } return z; }
-
指针数组 数组指针 函数指针 函数指针数组 指向函数指针数组的指针
2017-11-29 17:24:50指针数组 首先从名字就可以知道这是一个数组,是存放指针的数组。 先看几种指针数组: int * 数组指针 函数指针 函数指针数组 指向函数指针数组的指针指针数组
首先从名字就可以知道这是一个数组,是存放指针的数组。先看几种指针数组:
因为 [ ] 的优先级是高于 * 的,所以数组名会先于 [ ] 相结合组成数组。 再于 int * / char *类型结合,组成存放该类型的数组。int *arr1[10]; char *arr2[5]; char **arr3[6];
指针数组因为存放的都是指针所以不管是 int * char * 还是 char ** 其大小都是四个字节。数组指针
从名字看就知道是指针,是指向一个数组的指针。
int (*p)[10];
由数组指针知道 [ ] 的优先级是高于 * 的,所以用()来提升指针先结合。char (*p)[10];
int (*p)[10]; // 是一个指向10个整形的一维数组的指针 char (*p)[10]; // 是一个指向10个字符型的一维数组的指针
数组的存储方式
因为指向的是一个数组,所以大小由数组决定函数指针
指向函数的指针,通俗的说也就是函数的地址void(*pfun)();
pfun 先和*结合,说明pfun是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void也就是说 其可以保存函数的地址函数指针数组
parr先与 [ ]结合,说明是一个数组,再与*结合,说明数组存放的是指针,指针都指向的是函数int (*parr[3])();
函数指针数组的使用(转移表)例子:计算器#include<stdio.h> int add( x, y) { return x + y; } int sub(x,y) { return x - y; } int mul(x,y) { return x * y; } int div(x,y) { return x / y; } int main() { int x, y; int input = 1; int ret = 0; int(*parr[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表 while (input) { printf("*******************************\n"); printf("***** 1.add 2.sub *****\n"); printf("***** 3.mul 4.div *****\n"); printf("*******************************\n"); printf("请选择:"); scanf("%d", &input); if ((input > 0 && input < 5)) { printf("输入你要计算的数:\n"); scanf("%d %d", &x, &y); ret = (*parr[input])(x, y); } else printf("输入有误!\n"); printf("%d\n", ret); } return 0; }
输出结果:指向函数指针数组的指针
这是一个指针,指向一个数组,这个数组是一个函数指针数组,数组中的元素都是函数指针
定义方式:
#include<stdio.h> void test(const char *str) { printf("%s\n", str); } int main() { void (*pfun)(const char*) = test; //函数指针pfun void (*pfunArr[5])(const char* str); //函数指针的数组pfunArr void (*(*ppfunArr)[10])(const char* str) = &pfunArr; //指向函数指针数组pfunArr的指针ppfunArr return 0; }
-
指针函数和函数指针
2019-03-30 16:21:36很多人因为搞不清这两个概念,干脆就避而远之,我刚接触C语言的时候对这两个概念也比较模糊,特别是当指针函数、函数指针、函数指针变量、函数指针数组放在一块的时候,能把强迫症的人活活逼疯。 其实如果理解了这些...概述
指针函数和函数指针是C语言里两个比较绕的概念。但是不仅面试题爱考,实际应用中也比较广泛。很多人因为搞不清这两个概念,干脆就避而远之,我刚接触C语言的时候对这两个概念也比较模糊,特别是当指针函数、函数指针、函数指针变量、函数指针数组放在一块的时候,能把强迫症的人活活逼疯。
其实如果理解了这些概念的本质,是不需要死记硬背的,理解起来也比较容易。指针函数
指针函数: 顾名思义,它的本质是一个函数,不过它的返回值是一个指针。其声明的形式如下所示:
ret *func(args, ...);
其中,
func
是一个函数,args
是形参列表,ret *
作为一个整体,是func
函数的返回值,是一个指针的形式。
下面举一个具体的实例来做说明:文件:pointer_func.c
# include <stdio.h> # include <stdlib.h> int * func_sum(int n) { if (n < 0) { printf("error:n must be > 0\n"); exit(-1); } static int sum = 0; int *p = ∑ for (int i = 0; i < n; i++) { sum += i; } return p; } int main(void) { int num = 0; printf("please input one number:"); scanf("%d", &num); int *p = func_sum(num); printf("sum:%d\n", *p); return 0; }
上例就是一个指针函数的例子,其中,
int * func_sum(int n)
就是一个指针函数, 其功能十分简单,是根据传入的参数n
,来计算从0到n的所有自然数的和,其结果通过指针的形式返回给调用方。
以上代码的运行结果如下所示:
如果上述代码使用普通的局部变量来实现,也是可以的,如下所示:文件:pointer_func2.c
# include <stdio.h> # include <stdlib.h> int func_sum2(int n) { if (n < 0) { printf("error:n must be > 0\n"); exit(-1); } int sum = 0; int i = 0; for (i = 0; i < n; i++) { sum += i; } return sum; } int main(void) { int num = 0; printf("please input one number:"); scanf("%d", &num); int ret = func_sum2(num); printf("sum2:%d\n", ret); return 0; }
本案例中,
func_sum2
函数的功能与指针函数所实现的功能完全一样。
不过在使用指针函数时,需要注意一点,相信细心地读者已经发现了,对比func_sum
和func_sum2
函数,除了返回值不一样之外,还有一个不同的地方在于,在func_sum
中,变量sum
使用的是静态局部变量,而func_sum2
函数中,变量sum
使用的则是普通的变量。
如果我们把指针函数的sum
定义为普通的局部变量,会是什么结果呢?不妨来试验一下:文件:pointer_func3.c
# include <stdio.h> # include <stdlib.h> int * func_sum(int n) { if (n < 0) { printf("error:n must be > 0\n"); exit(-1); } int sum = 0; int *p = ∑ for (int i = 0; i < n; i++) { sum += i; } return p; } int main(void) { int num = 0; printf("please input one number:"); scanf("%d", &num); int *p = func_sum(num); printf("sum:%d\n", *p); return 0; }
执行以上程序,发现仍然能得到正确的结果:
可是如果我们把main
函数里面稍微改动一下:int main(void) { int num = 0; printf("please input one number:"); scanf("%d", &num); int *p = func_sum(num); printf("wait for a while...\n"); //此处加一句打印 printf("sum:%d\n", *p); return 0; }
我们在输出
sum
之前打印一句话,这时看到得到的结果完全不是我们预先想象的样子,得到的并不是我们想要的答案。
为什么会出现上面的结果呢?
其实原因在于,一般的局部变量是存放于栈区的,当函数结束,栈区的变量就会释放掉,如果我们在函数内部定义一个变量,在使用一个指针去指向这个变量,当函数调用结束时,这个变量的空间就已经被释放,这时就算返回了该地址的指针,也不一定会得到正确的值。上面的示例中,在返回该指针后,立即访问,的确是得到了正确的结果,但这只是十分巧合的情况,如果我们等待一会儿再去访问该地址,很有可能该地址已经被其他的变量所占用,这时候得到的就不是我们想要的结果。甚至更严重的是,如果因此访问到了不可访问的内容,很有可能造成段错误等程序崩溃的情况。
因此,在使用指针函数的时候,一定要避免出现返回局部变量指针的情况。
那么为什么用了static
就可以避免这个问题呢?
原因是一旦使用了static
去修饰变量,那么该变量就变成了静态变量。而静态变量是存放在数据段的,它的生命周期存在于整个程序运行期间,只要程序没有结束,该变量就会一直存在,所以该指针就能一直访问到该变量。
因此,还有一种解决方案是使用全局变量,因为全局变量也是放在数据段的,但是并不推荐使用全局变量。函数指针
与指针函数不同,函数指针 的本质是一个指针,该指针的地址指向了一个函数,所以它是指向函数的指针。
我们知道,函数的定义是存在于代码段,因此,每个函数在代码段中,也有着自己的入口地址,函数指针就是指向代码段中函数入口地址的指针。
其声明形式如下所示:ret (*p)(args, ...);
其中,
ret
为返回值,*p
作为一个整体,代表的是指向该函数的指针,args
为形参列表。其中p
被称为函数指针变量 。关于函数指针的初始化
与数组类似,在数组中,数组名即代表着该数组的首地址,函数也是一样,函数名即是该数组的入口地址,因此,函数名就是该函数的函数指针。
因此,我们可以采用如下的初始化方式:函数指针变量 = 函数名;
下面还是以一个简单的例子来具体说明一下函数指针的应用:
文件:func_pointer.c
#include <stdio.h> int max(int a, int b) { return a > b ? a : b; } int main(void) { int (*p)(int, int); //函数指针的定义 //int (*p)(); //函数指针的另一种定义方式,不过不建议使用 //int (*p)(int a, int b); //也可以使用这种方式定义函数指针 p = max; //函数指针初始化 int ret = p(10, 15); //函数指针的调用 //int ret = (*max)(10,15); //int ret = (*p)(10,15); //以上两种写法与第一种写法是等价的,不过建议使用第一种方式 printf("max = %d \n", ret); return 0; }
上面这个函数的功能也十分简单,就是求两个数中较大的一个数。值得注意的是通过函数指针调用的方式。
首先代码里提供了3种函数指针定义的方式,这三种方式都是正确的,比较推荐第一种和第三种定义方式。然后对函数指针进行初始化,前面已经提到过了,直接将函数名赋值给函数指针变量名即可。
上述代码运行的结果如下:
调用的时候,既可以直接使用函数指针调用,也可以通过函数指针所指向的值去调用。(*p)
所代表的就是函数指针所指向的值,也就是函数本身,这样调用自然不会有问题。有兴趣的同学可以去试一试。为什么要使用函数指针?
那么,有不少人就觉得,本来很简单的函数调用,搞那么复杂干什么?其实在这样比较简单的代码实现中不容易看出来,当项目比较大,代码变得复杂了以后,函数指针就体现出了其优越性。
举个例子,如果我们要实现数组的排序,我们知道,常用的数组排序方法有很多种,比如快排,插入排序,冒泡排序,选择排序等,如果不管内部实现,你会发现,除了函数名不一样之外,返回值,包括函数入参都是相同的,这时候如果要调用不同的排序方法,就可以使用指针函数来实现,我们只需要修改函数指针初始化的地方,而不需要去修改每个调用的地方(特别是当调用特别频繁的时候)。回调函数
函数指针的一个非常典型的应用就是回调函数。
什么是回调函数?
回调函数就是一个通过指针函数调用的函数。其将函数指针作为一个参数,传递给另一个函数。
回调函数并不是由实现方直接调用,而是在特定的事件或条件发生时由另外一方来调用的。
同样我们来看一个回调函数的例子:文件:callback.c
#include<stdio.h> #include<stdlib.h> //函数功能:实现累加求和 int func_sum(int n) { int sum = 0; if (n < 0) { printf("n must be > 0\n"); exit(-1); } for (int i = 0; i < n; i++) { sum += i; } return sum; } //这个函数是回调函数,其中第二个参数为一个函数指针,通过该函数指针来调用求和函数,并把结果返回给主调函数 int callback(int n, int (*p)(int)) { return p(n); } int main(void) { int n = 0; printf("please input number:"); scanf("%d", &n); printf("the sum from 0 to %d is %d\n", n, callback(n, func_sum)); //此处直接调用回调函数,而不是直接调用func_sum函数 return 0; }
上面这个简单的demo就是一个比较典型的回调函数的例子。在这个程序中,回调函数
callback
无需关心func_sum
是怎么实现的,只需要去调用即可。
这样的好处就是,如果以后对求和函数有优化,比如新写了个func_sum2
函数的实现,我们只需要在调用回调函数的地方将函数指针指向func_sum2
即可,而无需去修改callback
函数内部。
以上代码的输出结果如下:
回调函数广泛用于开发场景中,比如信号函数、线程函数等,都使用到了回调函数的知识。 -
C++函数指针、指针函数、返回值为函数指针的函数浅谈
2018-10-30 21:54:43C++函数指针、指针函数、返回值为函数指针的函数浅谈 引言 函数指针、指针函数是C中重要而容易混淆的概念,博主将通过两个实例来说明这两个截然不同的概念。 而返回值为函数指针的指针函数就更难理解了,放在文章的... -
详解C语言指针函数、函数指针、函数指针数组
2018-07-09 11:08:24而在指针中,指针函数、函数指针、指针函数数组、函数指针数组、指向函数指针数组的指针等等概念看着又绕又头疼。本问总结了一下以上一些概念以及用法,并给出例程深化理解。 1. 指针函数 指针函数就是返回指针值... -
【恼人】——函数指针 函数指针数组 指向函数指针数组的指针
2018-04-21 00:12:34函数指针:函数指针是指向可执行代码段或调用可执行代码段的信息块的指针,而不是指向某种数据的指针。函数指针是将函数当做普通数据那样存储和管理。函数指针有一种固定的形式,就是包含一个确定的返回值类型和若干... -
指针 指针数组 指针数组的指针 数组指针 数组指针的数组 函数指针 指向函数指针数组的指针
2017-05-24 22:30:26指针 指针数组 指针数组的指针 数组指针 数组指针的数组 函数指针 函数指针数组 指向函数指针数组的指针 -
函数指针系列:用函数指针调用执行函数
2017-09-06 10:44:57函数指针 -
【c语言】理解指针数组、数组指针、函数指针、函数指针数组、函数指针数组的指针
2018-06-22 16:12:38三、函数指针 四、函数指针数组 五、指向函数指针数组的指针 目录 1.指针数组 2.数组指针 3.函数指针 4.函数指针数组 5.函数指针数组的指针 一、指针数组 1.是一个存放指针的数组 2.举一个... -
c++ 函数指针
2019-06-15 21:41:38函数指针基础: 1. 获取函数的地址 2. 声明一个函数指针 3.使用函数指针来调用函数 获取函数指针: 函数的地址就是函数名,要将函数作为参数进行传递,必须传递函数名。 声明函数指针 声明指针时,必须指定... -
函数指针和函数指针类型
2018-09-29 08:48:59函数指针 1. 定义 每一个函数都占用一段内存单元,它们有一个起始地址,指向函数入口地址的指针称为函数指针。 注意:函数指针的本质是一个指针变量,且指针指向的函数的入口地址 2. 语法 指向函数的指针变量... -
c语言函数指针_指针函数_返回值是函数指针
2019-03-06 17:38:43用函数指针作为函数的返回值 1.指针函数的定义 顾名思义,指针函数即返回指针的函数。其一般定义形式如下: 类型名 *函数名(函数参数表列); 其中,后缀运算符括号“()”表示这是一个函数,其前缀运算符星号“*”... -
18. 函数指针,函数指针数组与指向函数指针数组的指针
2018-04-30 19:25:52C语言是一门面向过程的语言,而面向过程最大的利器就是函数,今天我们就来研究一下函数指针相关的话题。 1. 函数指针 首先看一个例子。 #include <stdio.h> #include <stdlib... -
函数指针和指针函数
2018-09-25 22:14:37函数指针 函数指针是指向函数的指针变量,所以函数指针首先是指针变量,只不过他指向的是函数; C/CPP在编译时,默认分配给函数一个入口,该入口即是函数指针所要指向的地址,可以用为两个用途: 调用函数 做函数... -
函数指针、this指针、bind函数、成员函数指针
2018-03-20 12:56:02一、函数指针(不是类的成员函数): 1、指针变量也可以指向一个函数。C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址,这个函数入口地址就称为函数的指针。 2、函数指针的作用: (1)... -
普通函数指针、成员变量指针与成员函数指针
2018-06-13 20:29:13普通函数指针、成员变量指针与成员函数指针(一)普通函数指针首先先说普通函数指针,对于函数指针总是与指针函数混淆,那么他们的区别在哪呢?1、指针函数 int* f(int a;int b) //函数名为f,返回值为int类型的... -
C语言tips:函数指针及函数指针数组
2020-09-20 16:20:11C语言:函数指针及函数指针数组 自1972年C语言在贝尔实验室问世以来,经久不衰,堪称编程语言中的常青树。而C语言中的指针更是其中最重要也最难的部分,通过指针可以实现很多让人惊喜的事情。 我们都知道,在C语言...
-
ISE_FIFO_IP核接口测试(二)
-
Android 118道基础面试题,面试途中不卡题
-
unity项目开发资料
-
计算机网络原理练习题附加答案.docx
-
端到端的一體化數位供應鏈
-
在FPGA上实现CRC算法的verilog程序
-
【数据分析-随到随学】Spark理论及实战
-
VxWorks6.9参考文档(英文)
-
课程作业数据.xlsx
-
算法与计算机算法
-
[SNOI2020]字符串
-
leetcode 680 验证回文字符串 Ⅱ
-
生物化学备考适合研究生
-
selenium爬取微博
-
【Leetcode】35.搜索插入位置/C++
-
微服务系列第七十一季-Spring入门
-
(新)备战2021软考软件设计师学习教程培训套餐
-
(新)备战2021软考网络工程师历年真题培训套餐
-
C语言程序设计最重要的期末考试知识点.pdf
-
猫狗数据集 百度云资源