LV015-指针函数
一、指针函数简介
1. 什么是指针函数
在 C 语言中允许一个函数的返回值是一个指针(即地址),这种返回指针值的函数称为指针函数。
2. 怎么定义?
指针函数定义的一般形式如下:
<数据类型> *<函数名称>( <形式参数> )
{
语句块;
return [ address ];
}【注意】
(1)比起普通的自定义函数,在函数名称前多了一个 * 。
(2)返回值与一般自定义函数不同,指针函数的返回值必须是一个地址量。可以是全局变量的地址、 static 变量的地址、字符串常量的地址或者堆的地址。
不可以返回局部变量的地址,因为局部变量在函数运行完的时候,它所占用的内存被释放掉了,我们不能在主调函数中再访问该局部变量的地址,访问一段已经释放的内存,是一种非法操作。
【示例分析】
int *pfunc(int, int);(1) * 运算符的优先级低于 () ,所以 pfunc 先与 () 结合,说明这是一个函数,也就可以写成这样
int *( pfunc(int, int) );
---------------(2) pfunc 是一个函数,括号里边是两个 int 类型的形参列表,这说明 pfunc 函数带有两个 int 类型的形参,使用的时候需要传入两个 int 类型的实参。
(3)再看前边的 * ,说明这个函数的返回值是一个指针变量。
(4) * 前边的 int 表明返回的指针变量是 int 类型的。
总的来说,就是定义了一个名为 pfunc 的函数,函数有两个 int 类型的形参,函数返回一个指向整型数据的指针变量。
二、指针函数的应用
函数库中有字符串拼接函数,我们是不是可以考虑使用指针函数自己实现一个呢?
#include <stdio.h>
char *myStrcat(char *dest, const char *src); /* 函数声明 */
int main(int argc, char *argv[])
{
char str[50] = "Hello world!\x23welcome";
char str2[] = " here!";
printf("str[50] = %s, str2[] = %s\n", str, str2);
printf("myStrcat(str, str2) = %s, str2[] = %s\n", myStrcat(str, str2), str2);
return 0;
}
/* 字符串拼接函数 */
char *myStrcat(char *dest, const char *src)
{
char *restore = dest; /* 指向要返回的字符串的首地址 */
while(*dest++); /* 定位到 *dest 指向的字符串中的 \0 结束符处 */
dest--; /* 当 *dest = '\0'时循环结束,但是dest指向了 '\0' 后边的地址*/
while(*src)
{
// printf("*dest=%s, *src=%c----------", restore, *src);
*dest++ = *src++;
// printf("*dest=%s, *src=%c\n", restore, *src);
}
*dest = '\0'; /* 结尾补充一个'\0',可以预防字符串中间有 '\0' 的情况 */
return restore;
}然后在终端运行以下命令:
gcc test.c -Wall # 编译程序
./a.out # 执行可执行程序接着我们会在终端中看到以下输出信息:
str[50] = Hello world!#welcome, str2[] = here!
myStrcat(str, str2) = Hello world!#welcome here!, str2[] = here!三、指针函数与函数指针
| 指针函数 | 函数指针 | |
| 是什么? | 是函数 | 是指针 |
| 基本形式 | int *pfunc(int, int); | int (*pfunc)(int, int); |
| 分析过程 | - (1)* 运算符的优先级低于 () ,所以 pfunc 先与 () 结合,说明这是一个函数,也就可以写成这样:int *(pfunc(int, int)); - (2)pfunc 是一个函数,括号里边是两个 int 类型的形参列表,这说明 pfunc 函数带有两个 int 类型的形参,使用的时候需要传入两个 int 类型的实参。 - (3)再看前边的 * ,说明这个函数的返回值是一个指针变量。 - (4)* 前边的 int 表明返回的指针变量是 int 类型的。 总的来说,就是定义了一个名为 pfunc 的函数,函数有两个 int 类型的形参,函数返回一个指向整型数据的指针变量。 | - (1)这里还是先看 pfunc ,通过 () 强行与 * 相结合,这就意味着 pfunc 是一个指针变量。 - (2)接着后边是 (int, int) ,这说明这个指针变量指向了一个函数,函数可以带两个 int 类型的参数。 - (3)最前边的 int 表明,函数的返回值是 int 类型。 总的来说,就是定义了一个名为 pfunc 的指针变量,这个指针变量可以指向返回值为 int 类型,且带有两个 int 类型参数的函数。 |
【结论】根据上边的分析,两个语句,仅仅是一个 () 的区别,所代表的含义就完全不同了,由对两个语句的分析可知,函数指针本质上还是一个指针,它可以指向一个函数;而指针函数本质上是一个函数,它可以返回一个指针。
四、函数指针作为返回值?
1. 基本形式
上边分析完函数指针和指针函数后,想到了点变态的东西,就是,函数指针既然是一个指针,而指针函数既然是一个函数,返回值为指针,那么它俩结合一下,后边还真遇到了这样的函数。既然后边会遇到,那就在这里总结一下,分析一下吗,彻底搞明白好啦。
直接找个例子分析一下吧:
int (*func(int))(int *, int);(1)先看名称,也就是 func ,由于 * 的优先级低于 () ,所以 func 与 () 结合,说明这是一个函数,所以也就可以写成这样了:
int ( *(func(int) )(int *, int);
------------(2) () 里边的 int 表示函数 func 带有一个 int 类型的形参。
(3)接着就是 * ,这表示这个函数的返回值是一个指针变量,然后就该最后的一个括号了,也就是
int ( *(func(int) )( int *, int );
------------(4)我们已经分析出来第一个 () 中是一个指针变量,那我们用 *p 暂时代替一下:
int (*p)( int *, int );
------------这样是不是就清楚多了呢?前边的 *p 是一个指针变量,后边的 () 表示这是一个函数,也就是说 p 指向了一个函数。
(5)最后的 () 里边表示这个函数有两个参数,一个是 int 类型指针,传输参数的时候需要传一个指针变量,另一个是 int 类型变量。结合 (4) 就是有一个指针 p 指向一个带有两个参数的函数。
(6)最前边的 int 表示 p 指向的函数的返回值为 int 类型。
总的来说就是,定义了一个指针函数,函数名为 func ,函数带有一个 int 类型的参数,这个函数的返回值是一个指针变量,函数返回的指针变量又是一个函数指针,它可以指向一个带有两个参数的且返回值为 int 类型的函数。这就是前边说的,定义了一个指针函数,指针函数返回一个可以指向函数指针。
Tips:这是一个返回函数指针的函数声明,即它返回的是一个函数指针,可以用这个指针再去调用它指向的函数。
mdint (*func(int))(int *, int); └──┬──┘ │ ┌─────┴─────┐ │ func 是一个函数(参数为 int) │ 返回一个指针 │ 指向一个函数(参数为 int *, int) │ 该函数返回 int └───────────┘分步解读:
步骤 看的方向 内容 含义 1 找标识符 funcfunc 是主体 2 向右 (int)func 是函数,参数是 int3 向左 *func 返回指针 4 向右 (int *, int)指针指向函数,参数是 (int *, int)5 向左 int被指向的函数返回 int
简直是难以理解,那有什么办法简化嘛?当然有啦,哈哈哈。还记得 typedef 吧,我看我的笔记似乎是记在用户自定义类型那了,但是问题不大,我们接下来就看一看这个关键字是如何简化上边的,额。。。就这样吧。
上边我们分析到第 (4) 步的时候,我们用了一个指针 FuncPtr 代替了 func(int) ,所以就成了下边的这个样子:
int (*FuncPtr)(int *, int);其实 FuncPtr 就是一个函数指针变量,可以指向一个函数带有两个参数的返回值为 int 类型的函数,我们使用 typedef 声明一下,将他变为一个类型:
typedef int (*FuncPtr)(int *, int);这样就相当于,我们定义了一个函数指针 p 的类型,它可以直接取代 int (xxx)(int *, int) 类型的函数指针,于是,上边的指针函数返回的函数指针就可以用新定义的类型 p 来定义了:
FuncPtr func(int);FuncPtr 就代表返回值的类型,就等价于 int (xxx)(int *, int) 。我是这样的理解的,也有可能理解的不到位,后边有什么问题的话会再修正。
2. 使用实例
#include <stdio.h>
// 两个候选函数,签名都是 int (int *, int)
int sum(int *arr, int n) {
int total = 0;
for (int i = 0; i < n; i++) total += arr[i];
return total;
}
int max(int *arr, int n) {
int m = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] > m) m = arr[i];
}
return m;
}
// 返回函数指针的函数
int (*func(int choice))(int *, int) {
if (choice == 1) {
return sum; // 返回 sum 函数的指针
} else {
return max; // 返回 max 函数的指针
}
}
int main(void) {
int arr[] = {1, 5, 3, 9, 2};
// 获取 sum 函数指针并调用
int (*f1)(int *, int) = func(1);
printf("sum = %d\n", f1(arr, 5)); // 输出 20
// 获取 max 函数指针并调用
int (*f2)(int *, int) = func(2);
printf("max = %d\n", f2(arr, 5)); // 输出 9
// 也可以直接调用
printf("直接调用 sum = %d\n", func(1)(arr, 5));
return 0;
}将会输出:
sum = 20
max = 9
直接调用 sum = 20