掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!

简介: 掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!

🍁博客主页:江池俊的博客

💫收录专栏:C语言进阶之路

💡代码仓库:江池俊的代码仓库

🎪我的社区:GeekHub

🎉欢迎大家点赞👍评论📝收藏⭐


一、函数指针



在C语言中,函数是一等公民,可以像其他变量一样被传递和使用。而函数指针就是指向函数的指针变量,可以用来调用函数。本文将介绍函数指针的定义、使用方法以及注意事项。

函数指针的定义格式为:

返回值类型 (*指针变量名)(参数列表);


其中,返回值类型表示函数的返回值类型,指针变量名是指向函数的指针变量的名称,参数列表表示函数的参数类型和数量。


例如,定义一个指向返回值为int、参数为两个int类型的函数的指针:

int (*pAdd)(int, int);


这个指针可以指向任何返回值为int、参数为两个int类型的函数。

先看一段代码:

#include <stdio.h>
void test()
{
 printf("hehe\n");
}
int main()
{
 printf("%p\n", test);
 printf("%p\n", &test);
 return 0;
}


输出结果:

856dd7afa804493398b4d18b78c3004f.png

输出的是两个地址,这两个地址是 test 函数的地址。

那我们的函数的地址要想保存起来,怎么保存?


下面我们看代码:

void test()
{
 printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();


首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?

答案是:


pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。


阅读两段有趣的代码:

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);


这两段代码都涉及了函数指针的用法,让我们逐一来解释它们:


代码1:


(*(void (*)())0)();


这段代码是一个函数指针调用的例子。让我们逐步分解它:

  • void (*)() 表示一个函数指针类型,它指向一个不接受任何参数(void),并且返回类型为 void 的函数。
    (void (*)())0
    这里的(void (*)())实际上是一个强制转化的操作,它是将0强制转化为函数指针类型,即是将函数指针初始化为一个地址为 0 的空指针,也就是一个无效的函数指针。
    (*(void (*)())0)();
    则是将这个无效的函数指针进行了间接调用,实际上是试图调用地址为 0 的函数,这通常会导致程序崩溃(因为操作系统不允许在地址 0 处执行代码,会触发段错误)。


这段代码在 C 语言中属于未定义行为,不应该在实际代码中使用,因为它可能导致程序的崩溃或其他不可预测的行为。


代码2:


void (*signal(int, void(*)(int)))(int);
//我们也可以将它简化为以下这种形式:
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);


这段代码涉及的是 C 语言中的信号处理函数 signal 的声明。让我们逐步解释它:

  • void(*)(int) 表示一个函数指针类型,它指向一个接受一个 int 参数并返回 void 的函数。
  • signal 是一个函数,它接受两个参数:一个 int 参数和一个函数指针参数,然后返回一个与上述函数指针类型匹配的函数指针。


所以整个代码声明的含义是:signal 是一个函数,它接受一个 int参数和一个函数指针参数,返回一个函数指针,该函数指针指向一个接受一个 int 参数并返回 void 的函数,这个函数通常用于处理信号。


这段代码通常用于在 C 语言中设置信号处理函数,以便在程序接收到特定信号时执行特定的操作。


请注意,这里只是声明了 signal 函数的原型,实际使用时需要根据具体情况编写函数体。

:推荐《C陷阱和缺陷》


这本书中提及这两个代码。


二、函数指针数组



函数指针数组是 C 语言中一个强大且常用的工具,用于存储指向不同函数的指针,允许根据需要调用特定的函数。在本文中,我们将深入介绍函数指针数组的概念、用途和实例,帮助你理解并充分利用这一重要的 C 语言特性。


什么是函数指针数组?


数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组


比如:

int *arr[10];
//数组的每个元素是int*


那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];


答案是:parr1

parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?

int (*)() 类型的函数指针。

函数指针数组的用途:转移表


总之,函数指针数组实际上是一个数组,其元素都是指向函数的指针。这使得我们可以将不同的函数存储在数组中,并通过索引来调用特定的函数。这种灵活性使得函数指针数组在编写菜单驱动程序、状态机、回调机制等方面非常有用。以下是一个简单的示例:

#include <stdio.h>
void func1() 
{
    printf("调用 func1函数\n");
}
void func2() 
{
    printf("调用 func2函数\n");
}
int main() 
{
    void (*funcPtrArray[2])() = {func1, func2};//函数指针数组funcPtrArray
    funcPtrArray[0](); //通过函数指针数组调用 func1
    funcPtrArray[1](); //通过函数指针数组调用 func2
    return 0;
}


为什么使用函数指针数组?


函数指针数组在以下情况下非常有用:

  1. 菜单驱动程序: 当需要实现一个用户界面,允许用户从菜单中选择不同的操作时,函数指针数组可以用来存储每个操作的处理函数。
    状态机:
    在状态机的实现中,可以使用函数指针数组来存储每个状态的处理函数,从而实现状态转换时的操作。
  2. 回调机制: 当你需要在某个事件发生时调用不同的函数,比如事件处理、信号处理等,函数指针数组提供了一种简洁的方式。
    动态选择算法:
    如果你有多个算法实现,但在运行时决定使用哪一个算法,函数指针数组可以帮助你实现动态选择算法。


函数指针数组的基本用法


让我们通过一个简单的例子来演示函数指针数组的基本用法:实现一个简单的计算器,允许用户选择不同的操作。

例子:(计算机)

#include <stdio.h>
int add(int a, int b)
{
    return a + b;
}
int subtract(int a, int b)
{
    return a - b;
}
int multiply(int a, int b)
{
    return a * b;
}
int division(int a, int b)
{
    return a / b;
}
int main()
{
    int x, y;
    int input = 1;
    int restult = 0;
    // 定义函数指针数组,存储不同的操作函数
    //转移表
   int(*operation[])(int x, int y) = { 0,add,subtract,multiply,division };
    while (input)
    {
        printf("\n*************************\n");
        printf(" 1:add           2:subtract \n");
        printf(" 3:multiply      4:division \n");
        printf("*************************\n");
        printf("请选择:");
        scanf("%d", &input);
        if ((input <= 4 && input >= 1))
        {
            printf("输入操作数:");
            scanf("%d %d", &x, &y);
            restult = (*operation[input])(x, y);// 调用选定的函数
            printf("restult = %d\n", restult);
        }
        else
            printf("输入有误,请重新输入\n");
    }
    return 0;
}


在上述示例中,我们定义了一个函数指针数组 operation,其中的元素分别指向 addsubtractmultiplydivision 函数。用户可以根据选择来执行不同的操作。


三、 指向函数指针数组的指针



指向函数指针数组的指针是什么?


指向函数指针数组的指针是一个指针,指针指向一个的数组,数组元素都是函数指针。这种指针提供了对函数指针数组的更高级别的访问方式,使我们能够更灵活地处理函数指针数组。以下是一个示例:

#include <stdio.h>
void func1() 
{
    printf("调用 func1函数\n");
}
void func2() 
{
    printf("调用 func2函数\n");
}
int main() 
{
    void (*funcPtrArray[2])() = {func1, func2};//函数指针的数组funcPtrArray
    void (*(*ptrToFuncPtrArray))() = funcPtrArray;//指向函数指针数组funcPtrArray的指针ptrToFuncPtrArray
    ptrToFuncPtrArray[0](); // 通过指向函数指针数组的指针调用 func1
    ptrToFuncPtrArray[1](); // 通过指向函数指针数组的指针调用 func2
    return 0;
}


为什么使用指向函数指针数组的指针?


指向函数指针数组的指针可能在日常编程中不常见,但在某些情况下非常有用。以下是一些使用情况:

  1. 函数指针数组的参数传递: 通过传递指向函数指针数组的指针作为参数,可以避免复制整个数组,从而提高效率。
    动态函数调用:
    使用指向函数指针数组的指针,可以在运行时根据条件选择不同的函数进行调用。
  2. 代码模块化: 当函数指针数组较大或需要在多个函数之间共享时,使用指向函数指针数组的指针可以提高代码的模块化性。
    函数指针数组的排序:
    可以使用指向函数指针数组的指针来执行对函数指针数组的排序操作,以实现按照某种规则调用函数。


总结



  • 在本篇博客中,我们深入探讨了 C语言中的三个重要概念:函数指针、函数指针数组和指向函数指针数组的指针。这些概念虽然可能听起来有些复杂,但它们为我们在C编程中提供了更大的灵活性和功能。
    函数指针允许我们将函数作为数据,传递给其他函数或存储在数据结构中。通过使用函数指针,我们可以实现更动态和可配置的程序设计,同时避免代码的重复。
    函数指针数组进一步扩展了这种灵活性,允许我们将多个函数指针组织在一个数组中,以便在运行时根据需要选择和调用不同的函数。这在构建可插拔的模块和实现动态行为时特别有用。
    最后,我们介绍了指向函数指针数组的指针,这为我们提供了一种更高级的访问方式,使得处理函数指针数组变得更加优雅。它可以应用于参数传递、动态函数调用、代码模块化以及对函数指针数组的排序等各种场景,从而增强了程序的模块性、可维护性和性能。


🔥今天的分享就到这里, 如果觉得博主的文章还不错的话, 请👍三连支持一下博主哦🤞

image.png


目录
相关文章
|
2月前
|
搜索推荐 C语言
指针与数组
指针与数组
51 9
|
2月前
|
算法 Java
双指针在数组遍历中的应用
文章深入探讨了双指针技术在数组遍历中的应用,通过实战例子详细解释了快慢指针和首尾指针的不同用法,并提供了解决LeetCode相关问题的Java代码实现。
|
2月前
|
存储 程序员 C语言
指针的高级应用:指针数组、数组指针、函数指针等。
指针的高级应用:指针数组、数组指针、函数指针等。
81 0
|
3月前
|
运维
开发与运维数组问题之指针的加减法意义如何解决
开发与运维数组问题之指针的加减法意义如何解决
38 7
|
3月前
|
C++ 索引 运维
开发与运维数组问题之在C++中数组名和指针是等价如何解决
开发与运维数组问题之在C++中数组名和指针是等价如何解决
22 6
|
3月前
|
存储 C++ 运维
开发与运维数组问题之指针的定义语法如何解决
开发与运维数组问题之指针的定义语法如何解决
27 6
|
4月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
39 0
|
4月前
|
C语言
C语言中的函数指针、指针函数与函数回调
C语言中的函数指针、指针函数与函数回调
|
4月前
|
存储 C语言
C语言中的多级指针、指针数组与数组指针
C语言中的多级指针、指针数组与数组指针
|
4月前
|
存储 C语言
C语言数组指针详解与应用
C语言数组指针详解与应用