文章

函数指针

一.什么是函数指针

首先用函数指针变量调用函数

一个函数,在编译的时候,系统会给这个函数分配一个入口地址,这个入口地址就称为函数的指针(地址),既然有地址,我们可以定义一个指针变量,指向该函数,然后通过该指针变量调用该函数

1
2
3
4
5
6
7
8
9
int max(int a, int b)
{
    if(a < b)
        return b;
    return a;
}

int a = max(10 ,20);
cout << a << endl;

这是正常的函数调用,现在用函数指针看看

1
2
3
4
int (*p)(int x, int y); //定义一个函数指针变量
p = max;
int a = (*p)(10, 20);//调用*p就是调用函数max,p指向函数max的入口,等价于a = max(10,20),这里的调用只是用*p取代了函数名,p不能指向函数中间的某条语句,所以*(p+1)不合法,其实这里的(*p)的*是可以省略的,比如a = p(10, 20)
cout << a << endl;

对比一下

1
2
3
4
5
6
7
8
9
10
原来是
int max(int a, int b)
现在是
int (*p)(int x, int y);
相当于max 替换成(*p)

原来调用是
max(10, 20)
现在是
(*p)(10, 20);

记得

1
int (*p)(int x, int y);

不能写成

1
int *p(int x, int y);

因为括号优先级比*的优先级要高,如果这样写的话,这就是函数说明了,这里的int *就表示这个函数的返回值是指向整型变量的指针

所以*p两侧的括号不可以省略,有括号表示*和p先结合代表一个指针变量,然后再和后面的括号结合表示此指针变量指向函数

当然这个可以写的简单一些

1
2
3
int (*p)(int x, int y);
可以写成
int (*p)(int, int);

像上面写的

1
p = max

将函数max的入口地址赋给指针变量p,函数名代表函数的入口地址,现在p就是指向函数max的指针变量,p和max都指向函数的开头

或者这样写也行

1
p = &max

p = max和p = &max是一样的

还有就是调用的时候,可以等价写

就是a=(*p)(10, 20)和a = p(10, 20)等价

其实如果真的调试程序的话,你看p=max这个,发现max是一个地址,p是一个地址

但其实真正函数的地址是p显示的,因为VS内部对函数有一些处理手段,会有一张表,max显示的地址只不过是表里的地址而已

但你如果输出p和max,发现他们两个是一样的,都是p显示的地址,也就是函数真正的地址

总结下

a)函数指针的一般形式:

数据类型标识符 (*指针变量名)(形参列表) 比如:int (*p)(int x, int y);

其中数据类型标识符就是指函数的返回值类型,形参列表里可以只有类型说明符,多个类型说明符之间用逗号分隔

我们可以通过函数指针指向不同的函数,来达到调用不同函数的目的,这个是有实际用途的

b)函数的调用,可以通过函数名,也可以通过函数指针调用:max(10, 20)和(*p)(10, 20);

c)对指向函数的指针变量p,做一些像p++,p–,p+n等运算都不可,也没有意义

二.把指向函数的指针变量作为函数参数

指向函数的指针变量也可以作为另外一个函数FuncB的参数,从而实现函数地址的传递,也就是在FuncB函数中调用该函数指针变量所指向的函数的目的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int max(int a, int b)
{
    if(a < b)
        return b;
    return a;
}

int wwmax(int x, int y, int (*p)(int x, int y))
{
    int result = p(x, y);
    return result;
}
或者把wwmax这样写
int wwmax(
    int x, 
    int y, 
    int (*p)(int x, int y) //形参就是一个函数指针
	)
{
    int result = p(x, y);调用函数指针p所指向的函数
    return result;
}

int main()
{
    int c;
    c = wwmax(1, 2, max);
    
    int (*midFunc)(int, int);
    midFunc = max;
    c = wwmax(1, 2, midFunc);
    //这三行代码和c = wwmax(1, 2, max);效果是一样的,因为midFunc就是max
}

那有人会问为什么我们不能在wwmax直接调用max函数呢,而要用函数指针

原因是这样滴,看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int max(int a, int b)
{
    if(a < b)
        return b;
    return a;
}
int min(int a, int b)
{
    if(a < b)
        return a;
    return b;
}
int wwmax(
    int x, 
    int y, 
    int (*p)(int x, int y) //形参就是一个函数指针
	)
{
    int result = p(x, y);调用函数指针p所指向的函数
    return result;
}
int main()
{
    int c;
    c = wwmax(1, 2, max);
   	c = wwmax(1, 2, min);
}

如果我们有min函数,那我们如果像刚才说的,直接在wwmax里面调用max函数,那有了min的话,不得还在wwmax里面修改嘛,修改或者添加一个min,这个也太费事了

而写成函数指针的形式来调用,你看我们wwmax代码不用改,直接在调用的时候传就可以c = wwmax(1, 2, min);

三.返回指针值的函数

一般的返回,是return; return 1;这种的

但是函数中也可以返回指针型数据,也就是地址

返回指针值的函数的一般定义形式:

数据类型 *函数名(参数列表): int *p(int x, int y);

其中p是函数名,()优先级高于*,因此p先和()结合,这就是函数形式,返回值为整型 指针

是不是有点熟悉,就是上面讲的

记得

1
int (*p)(int x, int y);

不能写成

1
int *p(int x, int y);

上面叫做,定义一个函数指针变量

下面叫做,返回指针值的函数

下面开始说例子

1
2
3
4
5
6
7
8
9
10
11
12
//返回指针值的函数
int *add(int x, int y)
{
    int sum = x + y;
    return &sum;
}
int main()
{
    int *presult;
    presult = add(4, 5);
    cout << *presult << endl;
}

这样看是没有问题的,并且输出也正确,但这有个致命的问题

1
return &sum; //隐藏一个致命的问题,add函数调用完毕后,sum的内存会被系统回收

比如调试的时候会看到&sum内存是0x0041,但是add调用完后,其实这段内存已经不属于add了,这时候你去读这个内存虽然是正确的,能读出来是9,但其实这块内存已经不属于add了,这样读或者写是有风险的

所以绝对不可以把sum的内存地址返回到被调用函数中并加以使用

presult = add(4, 5);*presult;也就是在执行add后,presult指向的内存已经不归你所有,你不应该从中取得值或者给他赋值

那要怎么改呢,先简单说一种,剩下的以后再说

第一种方式,就是把sum改成全局变量,这个变量占用的内存一直存在,能被我控制,直到程序结束

本文由作者按照 CC BY 4.0 进行授权