Skip to content

LV015-指针函数

一、指针函数简介

1. 什么是指针函数

在 C 语言中允许一个函数的返回值是一个指针(即地址),这种返回指针值的函数称为指针函数

2. 怎么定义?

指针函数定义的一般形式如下:

c
<数据类型> *<函数名称>( <形式参数> ) 
{
	语句块;
	return [ address ];
}

【注意】

(1)比起普通的自定义函数,在函数名称前多了一个 * 。

(2)返回值与一般自定义函数不同,指针函数的返回值必须是一个地址量。可以是全局变量的地址、 static 变量的地址、字符串常量的地址或者堆的地址。

不可以返回局部变量的地址,因为局部变量在函数运行完的时候,它所占用的内存被释放掉了,我们不能在主调函数中再访问该局部变量的地址,访问一段已经释放的内存,是一种非法操作

【示例分析】

c
int *pfunc(int, int);

(1) * 运算符的优先级低于 () ,所以 pfunc 先与 () 结合,说明这是一个函数,也就可以写成这样

c
int *( pfunc(int, int) );
      ---------------

(2) pfunc 是一个函数,括号里边是两个 int 类型的形参列表,这说明 pfunc 函数带有两个 int 类型的形参,使用的时候需要传入两个 int 类型的实参。

(3)再看前边的 * ,说明这个函数的返回值是一个指针变量。

(4) * 前边的 int 表明返回的指针变量是 int 类型的。

总的来说,就是定义了一个名为 pfunc 的函数,函数有两个 int 类型的形参,函数返回一个指向整型数据的指针变量。

二、指针函数的应用

函数库中有字符串拼接函数,我们是不是可以考虑使用指针函数自己实现一个呢?

c
#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;
}

然后在终端运行以下命令:

shell
gcc test.c -Wall # 编译程序
./a.out          # 执行可执行程序

接着我们会在终端中看到以下输出信息:

shell
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. 基本形式

上边分析完函数指针和指针函数后,想到了点变态的东西,就是,函数指针既然是一个指针,而指针函数既然是一个函数,返回值为指针,那么它俩结合一下,后边还真遇到了这样的函数。既然后边会遇到,那就在这里总结一下,分析一下吗,彻底搞明白好啦。

直接找个例子分析一下吧:

c
int (*func(int))(int *, int);

(1)先看名称,也就是 func ,由于 * 的优先级低于 () ,所以 func 与 () 结合,说明这是一个函数,所以也就可以写成这样了:

c
int (  *(func(int)  )(int *, int);
       ------------

(2) () 里边的 int 表示函数 func 带有一个 int 类型的形参。

(3)接着就是 * ,这表示这个函数的返回值是一个指针变量,然后就该最后的一个括号了,也就是

c
int (  *(func(int)  )( int *, int );
                      ------------

(4)我们已经分析出来第一个 () 中是一个指针变量,那我们用 *p 暂时代替一下:

c
int (*p)( int *, int );
         ------------

这样是不是就清楚多了呢?前边的 *p 是一个指针变量,后边的 () 表示这是一个函数,也就是说 p 指向了一个函数。

(5)最后的 () 里边表示这个函数有两个参数,一个是 int 类型指针,传输参数的时候需要传一个指针变量,另一个是 int 类型变量。结合 (4) 就是有一个指针 p 指向一个带有两个参数的函数。

(6)最前边的 int 表示 p 指向的函数的返回值为 int 类型。

总的来说就是,定义了一个指针函数,函数名为 func ,函数带有一个 int 类型的参数,这个函数的返回值是一个指针变量,函数返回的指针变量又是一个函数指针,它可以指向一个带有两个参数的且返回值为 int 类型的函数。这就是前边说的,定义了一个指针函数,指针函数返回一个可以指向函数指针。

Tips:这是一个返回函数指针的函数声明,即它返回的是一个函数指针,可以用这个指针再去调用它指向的函数

md
int (*func(int))(int *, int);
     └──┬──┘

  ┌─────┴─────┐
  │  func 是一个函数(参数为 int)
  │  返回一个指针
  │  指向一个函数(参数为 int *, int)
  │  该函数返回 int
  └───────────┘

分步解读:

步骤看的方向内容含义
1找标识符funcfunc 是主体
2向右(int)func 是函数,参数是 int
3向左*func 返回指针
4向右(int *, int)指针指向函数,参数是 (int *, int)
5向左int被指向的函数返回 int

简直是难以理解,那有什么办法简化嘛?当然有啦,哈哈哈。还记得 typedef 吧,我看我的笔记似乎是记在用户自定义类型那了,但是问题不大,我们接下来就看一看这个关键字是如何简化上边的,额。。。就这样吧。

上边我们分析到第 (4) 步的时候,我们用了一个指针 FuncPtr 代替了 func(int) ,所以就成了下边的这个样子:

c
int (*FuncPtr)(int *, int);

其实 FuncPtr 就是一个函数指针变量,可以指向一个函数带有两个参数的返回值为 int 类型的函数,我们使用 typedef 声明一下,将他变为一个类型:

c
typedef int (*FuncPtr)(int *, int);

这样就相当于,我们定义了一个函数指针 p 的类型,它可以直接取代 int (xxx)(int *, int) 类型的函数指针,于是,上边的指针函数返回的函数指针就可以用新定义的类型 p 来定义了:

c
FuncPtr func(int);

FuncPtr 就代表返回值的类型,就等价于 int (xxx)(int *, int) 。我是这样的理解的,也有可能理解的不到位,后边有什么问题的话会再修正。

2. 使用实例

c
#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;
}

将会输出:

shell
sum = 20
max = 9
直接调用 sum = 20