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

news2024/10/5 18:26:04

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

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

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

🎪我的社区:GeekHub

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

在这里插入图片描述

文章目录

  • 一、函数指针
      • 代码1:
      • 代码2:
  • 二、函数指针数组
    • 什么是函数指针数组?
    • 为什么使用函数指针数组?
    • 函数指针数组的基本用法
  • 三、 指向函数指针数组的指针
    • 指向函数指针数组的指针是什么?
    • 为什么使用指向函数指针数组的指针?
  • 总结


一、函数指针

在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;
}

输出结果:
在这里插入图片描述
输出的是两个地址,这两个地址是 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. 状态机: 在状态机的实现中,可以使用函数指针数组来存储每个状态的处理函数,从而实现状态转换时的操作。

  3. 回调机制: 当你需要在某个事件发生时调用不同的函数,比如事件处理、信号处理等,函数指针数组提供了一种简洁的方式。

  4. 动态选择算法: 如果你有多个算法实现,但在运行时决定使用哪一个算法,函数指针数组可以帮助你实现动态选择算法。

函数指针数组的基本用法

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

#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. 动态函数调用: 使用指向函数指针数组的指针,可以在运行时根据条件选择不同的函数进行调用。

  3. 代码模块化: 当函数指针数组较大或需要在多个函数之间共享时,使用指向函数指针数组的指针可以提高代码的模块化性。

  4. 函数指针数组的排序: 可以使用指向函数指针数组的指针来执行对函数指针数组的排序操作,以实现按照某种规则调用函数。

总结

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

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/894105.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何恢复QQ聊天记录,教你3个好方法

有没有这样一款软件&#xff0c;你很久不使用但却不舍得卸载&#xff0c;这款软件便是QQ。作为腾讯最出色的社交产品&#xff0c;其影响力之大不言而喻。 虽然现在微信的普及程度远远超过了QQ&#xff0c;但大多数用户还保留着QQ。QQ在传输文件数据方面也确实更加方便&#xf…

【高级程序设计语言C++】位图

1. 位图1.1. 位图的模型图1.2. 判断一个数是否存在 2. 位图的实现2.1. 位图的基础模型2.2. 位图开辟空间的大小2.3. 位图的插入2.4. 位图的删除2.5. 位图的查询 3. 位图的应用4. 哈希切分5. 位图的优缺点 1. 位图 C中的位图&#xff08;Bitset&#xff09;是一种用于存储和操作…

Windows下问题定位

1、内存相关知识点&#xff1b; 1&#xff09;windows下32位进程&#xff0c;用户态为2G内存&#xff0c;内核态也为2G内存&#xff1b;却别于linux操作系统&#xff1b; 备注&#xff1a;可以通过命令行与管理员权限&#xff0c;启动3G的用户态空间&#xff0c;但是部…

【数学建模】-- 数学规划模型

概述&#xff1a; 什么是数学规划&#xff1f; 数学建模中的数学规划是指利用数学方法和技巧对问题进行数学建模&#xff0c;并通过数学规划模型求解最优解的过程。数学规划是一种数学优化方法&#xff0c;旨在找到使目标函数达到最大值或最小值的变量取值&#xff0c;同时满足…

免费内网穿透哪个好?

神卓互联是一种内网穿透技术&#xff0c;可以实现在外部网络访问公司内网的服务。通过建立一个加密的通道&#xff0c;神卓互联可以将内网的动态IP绑定技术&#xff0c;实现远程访问。 使用神卓互联进行内网穿透的步骤如下&#xff1a; 在公司内网中&#xff0c;安装并配置神卓…

Linux 进程间通信——有名管道和无名管道

一、管道的概念 当从一个进程连接数据流到另一个进程时&#xff0c;我们使用管道。通常把一个进程的输出通过管道连接到另一个进程的输入。 管道可以用来在两个进程之间传递数据&#xff0c;如&#xff1a; ps -ef | grep “bash”, 其中‘|’就是管道&#xff0c;其作用就是…

CH32V203 单片机 I2C 使用

CH32V203系列是基于32位RISC-V内核设计的工业级增强型低功耗通用微控制器&#xff0c;高性能&#xff0c;最高支持144MHz系统主频&#xff0c;低功耗&#xff0c;运行功耗低至45uA/MHz。CH32V203集成双路USB接口&#xff0c;支持USB Host主机及USB Device设备功能&#xff0c;具…

[NAS4]Tiny adversarial multi-objective one-shot neural architecture search

论文链接&#xff1a;https://arxiv.org/abs/2103.00363v1 代码链接&#xff1a; 摘要&#xff1a;移动设备中广泛使用的微小神经网络&#xff08;TNN&#xff09;容易受到对抗性攻击&#xff0c;对TNN鲁棒性的更先进研究需求也越来越大。 本文关注于如何在不损失模型精度的…

【win7Window】高仿Windows7系统窗体

特性&#xff1a; 任意拖拽到边界可以最大化、半屏放大双击边界可以水平、纵向最大化可以拖拽四边、四个顶点调整窗体尺寸可以最大化、还原、最小化、关闭支持双击标题栏最大化、还原支持双击左上角图标关闭窗体 win7Window源码 <template><div :class"$options…

JS的解析与Js2Py使用

JS的解析与Js2Py使用 JS的解析事件监听器搜索关键字请求关联JS文件 Js2PyJs2Py的简单使用安装Js2Py执行JavaScript代码调用JavaScript函数 Js2Py的应用示例创建JavaScript文件使用JavaScript JS的解析 在一个网站中&#xff0c;登录密码通常是会进行加密操作的&#xff0c;那么…

LeetCode--HOT100题(34)

目录 题目描述&#xff1a;94. 二叉树的中序遍历&#xff08;简单&#xff09;题目接口解题思路1代码解题思路2代码 PS: 题目描述&#xff1a;94. 二叉树的中序遍历&#xff08;简单&#xff09; 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 LeetCode做…

el-table根据容器大小自适应滚动条-修改滚动条样式

需求&#xff1a;父容器里有多个容器为上下级&#xff0c;之后浏览器在缩放的时候&#xff0c;上面容器高度改变了&#xff0c;所以el-table被挤压&#xff0c;如果el-table设置的是固定的高度&#xff0c;那么挤压后内容超出父容器&#xff0c;本文章就是解决这个问题 不自适…

拒绝无效内卷!新同事靠着这套大屏模板效率翻倍,搭上升职快车!

竞争是职场永恒的话题&#xff0c;太多人为了升职加薪卷生卷死&#xff0c;就拿我自己身边来说&#xff0c;这段时间我所在的小组有一个升职机会&#xff0c;好多老员工为了这个机会争得头破血流&#xff0c;一个在公司待了10年的老员工&#xff0c;直接天天加班到12点&#xf…

蓝牙耳机的发展

目录 1.蓝牙耳机的概念 2.蓝牙耳机的发展过程 3.蓝牙耳机的便利性 4.蓝牙耳机未来的发展趋势 1.蓝牙耳机的概念 蓝牙耳机是一种使用蓝牙无线技术连接到音频源设备&#xff08;如智能手机、平板电脑、电脑等&#xff09;的耳机。它们通过无线蓝牙信号接收音频数据&#xff0c…

MybatisPlus的使用

一. 关于注解的使用&#xff0c;官方地址&#xff1a; 注解 | MyBatis-PlusMyBatis-Plus 官方文档https://baomidou.com/pages/223848/#tablename 1.关于TableName的使用 假设我们不加TableName("tbl_employee")的话&#xff0c;那么数据库中的表名和类名相同的话&…

Qt下拉菜单

1&#xff0c;QComboBox 2&#xff0c;setMenu()---设置下拉菜单 AI对话未来丨智能写作对话: setMenu()是QWidget类的一个成员函数&#xff0c;在Qt中用于将一个菜单作为一个控件的下拉菜单设置。具体来说&#xff0c;它会把相应的菜单对象与该控件关联&#xff0c;并在控件上…

Mysql8.0为什么取消了缓存查询的功能

首先我们介绍一下MySQL的缓存机制 【MySQL缓存机制】简单的说就是缓存sql文本及查询结果&#xff0c;如果运行完全相同的SQL&#xff0c;服务器直接从缓存中取到结果&#xff0c;而不需要再去解析和执行SQL。 但如果表中任何数据或是结构发生改变&#xff0c;包括INSERT、UPD…

问道管理:放量打拐什么意思?常见的放量打拐三种形态?

成交量一直是股票交易中比较重要的目标&#xff0c;那么&#xff0c;放量打拐是什么意思&#xff1f;常见的放量打拐三种形状是什么&#xff1f;下面问道管理为我们预备了相关内容&#xff0c;以供参阅。 放量打拐什么意思&#xff1f; 放量是指股票成交量与前几个交易日比较显…

隐私文件夹怎么加密?隐私文件夹加密方法

在个人电脑中&#xff0c;我们经常会将一些个人隐私存放在电脑文件夹中&#xff0c;这些文件夹都需要加密保护。那么&#xff0c;隐私文件夹该怎么加密呢&#xff1f;下面我们就来了解一下吧。 超级秘密文件夹 隐私数据存放在文件夹中&#xff0c;最怕的就是被其他人发现。而如…

迅镭激光中标全球集装箱行业龙头中集集团10家子公司集采项目!

迅镭激光中标喜报频传!继连续中标工程机械、钢构、船舶、电力电气、新能源等多个行业龙头企业后&#xff0c;近日再次中标全球集装箱行业龙头中集集团(CIMC)&#xff0c;这与迅镭激光坚持高端、注重技术引领、十五年的技术沉淀和口碑积累密不可分。 日前&#xff0c;中集集团20…