当前位置: 首页 > news >正文

深入解析函数指针及其数组、typedef关键字应用技巧

目录

一、函数指针变量的创建

1、什么是函数指针变量?

2、函数是否有地址?

3、创建函数指针变量

4、函数指针类型解析

二、函数指针变量的使用

三、两段有趣的代码

1、解释 (*(void (*)())0)();

2、解释 void (*signal(int, void(*)(int)))(int);

四、typedef关键字

1、基本用法

2、对于数组指针和函数指针的重命名

3、使用typedef简化代码void (*signal(int, void(*)(int)))(int);

五、函数指针数组


一、函数指针变量的创建

1、什么是函数指针变量?

        类比整型指针和数组指针的概念,我们可以得出:函数指针变量是用来存放函数地址的变量,通过这个地址我们可以调用相应的函数。

2、函数是否有地址?

通过以下测试代码可以验证:

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

输出结果:

        结果表明函数确实有地址,且函数名就是函数的地址,也可以通过&函数名的方式获取函数地址。(重点!!!)

3、创建函数指针变量

函数指针变量的声明语法与数组指针类似:

void test() {printf("hehe\n");
}// 两种等效的函数指针声明方式
void (*pf1)() = &test;
void (*pf2)() = test;int Add(int x, int y) {return x + y;
}// 函数指针声明,参数名可省略
int (*pf3)(int, int) = Add;
int (*pf4)(int x, int y) = &Add;

4、函数指针类型解析

int (*pf3)(int x, int y)为例:int (*) (int x, int y) 为pf3函数指针变量的类型


二、函数指针变量的使用

通过函数指针调用函数的示例:

#include <stdio.h>int Add(int x, int y) {return x + y;
}int main() {int(*pf)(int, int) = Add;// 两种等效的调用方式printf("%d\n", (*pf)(2, 3));  // 显式解引用printf("%d\n", pf(3, 5));     // 隐式调用return 0;
}

输出结果:


三、两段有趣的代码

以下两段有趣的代码出自《C陷阱和缺陷》:

1、解释 (*(void (*)())0)();

这段代码的功能是:调用位于内存地址0处的函数

让我们逐步解析:

  1. void (*)():这是一个函数指针类型,表示"指向一个没有参数且返回void的函数的指针"。

  2. (void (*)())0:将整数值0强制转换为上述函数指针类型。这表示"把地址0当作一个函数的地址"。

  3. *(void (*)())0:解引用这个函数指针,得到位于地址0处的函数。

  4. (*(void (*)())0)():最后调用这个函数(通过函数指针调用)。

实际意义:这段代码尝试调用内存地址0处的函数。在嵌入式系统中,这可能是调用复位向量或启动代码的方式。但在大多数现代操作系统中,这会引发段错误(segmentation fault),因为地址0通常是被保护的区域。

2、解释 void (*signal(int, void(*)(int)))(int);

这是一个函数声明,声明了名为signal的函数。让我们分解它:

  1. 最内层:void(*)(int):这是一个函数指针类型,表示"指向一个接受int参数且返回void的函数的指针"。

  2. signal(int, void(*)(int)):signal是一个函数,它接受两个参数:一个int、一个上述类型的函数指针

  3. 整个声明:void (*signal(int, void(*)(int)))(int):表示signal函数返回一个函数指针,这个指针指向"接受int参数且返回void的函数"。

更易读的写法(使用typedef,下面会讲解):

typedef void (*sighandler_t)(int);  // 定义函数指针类型sighandler_t signal(int signum, sighandler_t handler);

实际意义:这是Unix/Linux系统中用于设置信号处理器的标准函数声明。它:

  • 接受一个信号编号(int)和一个处理该信号的函数指针

  • 返回之前为该信号设置的处理函数指针


四、typedef关键字

typedef用于类型重命名,可以简化复杂类型的声明。

1、基本用法

typedef unsigned int uint;  // 将unsigned int重命名为uint
typedef int* ptr_t;         // 将int*重命名为ptr_t

2、对于数组指针和函数指针的重命名

新的类型名必须在*的右边:

typedef int(*parr_t)[5];    // 将数组指针类型int(*)[5]重命名为parr_t
typedef void(*pfun_t)(int); // 将函数指针类型void(*)(int)重命名为pfun_t

3、使用typedef简化代码void (*signal(int, void(*)(int)))(int);

typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);

五、函数指针数组

数组是存放相同类型数据的存储空间。我们已经学过指针数组:

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

如果要存储函数地址,就需要使用函数指针数组。函数指针数组的定义方式:

int (*parr1[3])();   // 正确:包含3个函数指针的数组,每个指针指向返回int的无参函数

下面是错误用法:

int *parr2[3]();     // 错误:错误语法
int (*)() parr3[3];  // 错误:错误语法

解析parr1

  • parr1先与[]结合,说明它是一个数组

  • 数组的内容是int (*)()类型的函数指针

函数指针数组常用于实现状态机或回调函数机制,是C语言中一种强大的编程技术。

http://www.dtcms.com/a/335848.html

相关文章:

  • Go面试题及详细答案120题(21-40)
  • Pycharm Debug详解
  • C++ vector的使用
  • 自动驾驶中的传感器技术34——Lidar(9)
  • 前端项目练习-王者荣耀竞赛可视化大屏 -Vue纯前端静态页面项目
  • Springboot项目3种视图(JSP、Thymeleaf、Freemarker)演示
  • 图解直接插入排序C语言实现
  • 3.逻辑回归:从分类到正则化
  • pyecharts可视化图表组合组件_Grid:打造专业数据仪表盘
  • 矿物分类案列 (一)六种方法对数据的填充
  • C#WPF实战出真汁13--【营业查询】
  • 《设计模式》工厂方法模式
  • 数据结构与算法之 leetcode 98. 验证二叉搜索树 (前序,中序,后序遍历)
  • 影刀 RAP 迁移华为云备忘录数据到得到笔记
  • GitHub Copilot:AI编程助手的架构演进与真实世界影响
  • mac电脑开发嵌入式基于Clion(stm32CubeMX)
  • 深入了解linux系统—— 线程控制
  • IDE/去读懂STM32CubeMX 时钟配置图(有源/无源晶振、旁路/晶振模式、倍频/分频)
  • 三、k8s 1.29 之 安装2
  • 重温k8s基础概念知识系列三(工作负载)
  • 什么是GD库?PHP中7大类64个GD库函数用法详解
  • Kafka 面试题及详细答案100道(23-35)-- 核心机制2
  • 基础IO_系统文件IO | 重定向【Linux】
  • 《程序员修炼之道》第三四章读书笔记
  • 《算法导论》第 27 章 - 多线程算法
  • scikit-learn/sklearn学习|套索回归Lasso解读
  • Ansible 核心功能进阶:自动化任务的灵活控制与管理
  • 自由职业数据科学:从细分定位到规模化的实战路线
  • 记忆翻牌游戏 greenfoot 开发
  • 机器人经验学习1 杂记