C++——函数指针与指针函数

news2024/11/30 0:45:08

函数指针与指针函数

1. 初识

在这里插入图片描述
一个区分的小技巧:
“函数指针”中函数是用来修饰指针的,所以主体是指针,它主要是用来指向函数的。
“指针函数”中指针是用来修饰函数的,所以主体是函数,该函数的返回类型是指针。

举个简单的例子:

#include<iostream>
using namespace std;

int add(int a, int b) {
	return a + b;
}

int sub(int a, int b) {
	return a - b;
}

//函数指针
int (*f)(int, int);

//指针函数
int* multi(int a, int b) {
	int c = a*b;
	return &c;
}

int main() {
	int a = 3, b = 2;
	f = add; 
	cout << f(a, b) << endl;
	f = &sub;
	cout << f(a, b) << endl;
	int* c = multi(a, b);
	cout << *c << endl;
	return 0;
}

输出结果:
5
1
6

上面代码中,f = add;f = &sub 其实表达的是一个意思,函数指针f分别指向 add 和 sub。上面还有一个小细节,()的优先级比*高

2. 函数指针

函数凡是符合函数指针中的格式的,都可以传递给函数指针进行调用,具体说明,可以看下面代码例子。

函数指针一般用于回调函数、函数回调、函数指针数组等操作,提供了一种灵活的方式来实现动态的函数调用和扩展功能。

这里需要说明的一点就是回调函数和函数回调的区别
它们的区别跟“函数指针与指针函数的区别”同理,要看主体。函数回调是一种机制,回调函数是一个函数。在调用某个函数时,不是通过函数名,而是通过函数指针,这种方式就是函数回调,而这个“某个函数”就是回调函数。

2.1 回调函数和函数回调

#include <iostream>
using namespace std;

// 定义回调函数
void callbackFunction(int result) {
    cout << "回调函数被调用,result = " << result << endl;
}

// 调用回调函数的函数
void callCallbackFunction(int result, void (*callback)(int)) {
    // 执行一些操作
    cout << "执行一些操作" << endl;
    // 调用回调函数
    callback(result);
}

int main() {
    // 调用函数并传递回调函数
    callCallbackFunction(123, callbackFunction);
    return 0;
}

上面代码中callbackFunction就是回调函数,而在callCallbackFunction里面调用函数指针callback去调用回调函数,这种方式就是函数回调。

回调函数callbackFunction的返回类型是 void ,而参数类型是 int 。函数指针的返回类型也是 void,参数类型也是 int ,所以callbackFunction可以传递给函数指针。

它有另外一种写法:

#include <iostream>
using namespace std;

typedef void (*Callback)(int);

// 定义回调函数
void callbackFunction(int result) {
    cout << "回调函数被调用,result = " << result << endl;
}

// 调用回调函数的函数
void callCallbackFunction(int result, Callback callback) {
    // 执行一些操作
    cout << "执行一些操作" << endl;
    // 调用回调函数
    callback(result);
}

int main() {
    // 调用函数并传递回调函数
    callCallbackFunction(123, callbackFunction);
    return 0;
}

typedef void (*Callback)(int); 是一个函数指针类型的定义。让我逐步解释一下这个定义:

typedef:这个关键字用于给一个数据类型起一个别名,可以通过这个别名来声明变量。
void:这是函数的返回类型,表示函数没有返回值。
(*Callback):这是一个函数指针的声明,Callback 是这个函数指针的名称。
(int):这是函数指针所指向的函数的参数列表,int 表示这个函数接受一个 int 类型的参数。
综合起来看,typedef void (*Callback)(int); 是一个将函数指针类型命名为 Callback 的语句。它定义了一个函数指针类型,该函数指针可以指向一个返回值为空(void)的函数,该函数接受一个 int 类型的参数。

通过使用这个类型定义,我们可以方便地创建符合该函数指针类型的函数指针变量。例如,可以定义一个函数指针变量 Callback callback;,然后将其指向一个满足上述函数指针类型要求的函数,以便在需要时进行回调操作。

使用 typedef 可以简化代码,使代码更加易读和易于维护,提高了代码的可读性和可移植性。

2.2 函数指针数组

#include <iostream>

// 函数指针类型
typedef void (*PrintFunc)();

// 打印函数示例
void printHello() {
    std::cout << "Hello ";
}

void printWorld() {
    std::cout << "World!";
}

int main() {
    // 声明函数指针数组
    PrintFunc printFuncArray[] = { printHello, printWorld };

    // 遍历函数指针数组并调用函数
    for (int i = 0; i < sizeof(printFuncArray) / sizeof(printFuncArray[0]); ++i) {
        printFuncArray[i]();
    }

    std::cout << std::endl;

    return 0;
}

在上面的示例中,首先声明了一个函数指针类型 PrintFunc,它是一个指向返回类型为 void,不接受参数的函数指针。然后,我们定义了两个打印函数 printHello() 和 printWorld()。接下来,在 main 函数中,我们声明了一个函数指针数组 printFuncArray,其中存储了这两个打印函数的地址。最后,我们遍历函数指针数组,并通过函数指针调用函数来打印 “Hello World!”。

3. 指针函数

#include <iostream>

// 指针函数的原型
int* createArray(int size);

int main() {
    int size = 5;
    
    // 调用指针函数,分配内存并返回指针
    int* ptr = createArray(size);
    
    // 使用指针访问动态分配的数组
    for (int i = 0; i < size; ++i) {
        ptr[i] = i + 1;
    }
    
    // 打印数组元素
    for (int i = 0; i < size; ++i) {
        std::cout << ptr[i] << " ";
    }
    
    // 释放动态分配的内存
    delete[] ptr;
    
    return 0;
}

// 指针函数的定义
int* createArray(int size) {
    return new int[size];
}

总结: 指针函数在 C++ 中常用于动态内存分配、返回动态数据结构等场景。通过返回指针,我们可以在函数外部访问和操作函数内部动态分配的数据。需要注意的是,使用指针函数时要小心管理内存,避免内存泄漏和悬空指针等问题。

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

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

相关文章

A1048 Find Coins(测试点1)

Sample Input 1: 8 15 1 2 8 7 2 4 11 15 Sample Output 1: 4 11 Sample Input 2: 7 14 1 8 7 2 4 11 15 Sample Output 2: No Solution idea 测试点1&#xff1a;m比i小的情况处理小细节&#xff1a;i m/2时&#xff0c;注意i的出现次数必须大于一次 solution #in…

【数据结构--八大排序】之冒泡排序+选择排序+插入排序

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

【数据库——MySQL】(13)过程式对象程序设计——存储函数、错误处理以及事务管理

目录 1. 存储函数2. 存储函数的应用3. 错误处理4. 抛出异常5. 事务处理6. 事务隔离级7. 应用实例参考书籍 1. 存储函数 要 创建 存储函数&#xff0c;需要用到 CREATE 语句&#xff1a; CREATE FUNCTION 存储函数名([参数名 类型, ...])RETURNS 类型[存储函数体]注意&#xff1…

ESP32IDF — 硬件I2C使用教程

前言 &#xff08;1&#xff09;最近刚做完ESP32的一个模块的驱动移植&#xff0c;使用到了I2C。感觉ESP32的硬件I2C还是挺容易使用的。 &#xff08;2&#xff09;本文将只会介绍ESP32的硬件I2C使用&#xff0c;如果想知道软件I2C使用&#xff0c;可看其他的任意一款芯片软件I…

双指针算法——移动零

双指针算法——移动零&#x1f60e; 前言&#x1f64c;题目详情&#xff1a;图解分析&#xff1a;代码分享&#xff1a;B站讲解视频链接&#xff1a; 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧…

数据集笔记:华盛顿共享单车OD数据

2010~2022 共享单车OD数据 数据地址&#xff1a;Index of bucket "capitalbikeshare-data"

STM32F4X UCOSIII任务消息队列

STM32F4X UCOSIII任务消息队列 任务消息队列和内核消息队列对比内核消息队列内核消息队列 UCOSIII任务消息队列API任务消息队列发送函数任务消息队列接收函数 UCOSIII任务消息队列例程 之前的章节中讲解过消息队列这个机制&#xff0c;UCOSIII除了有内核消息队列之外&#xff0…

【数据结构】队列实现+层序遍历详解+一些练题

欢迎来到我的&#xff1a;世界 希望作者的文章对你有所帮助&#xff0c;有不足的地方还请指正&#xff0c;大家一起学习交流 ! 目录 前言队列的实现层序遍历详解强化练习1.判断是不是完全二叉树求二叉树的最大深度 总结 前言 国庆到了&#xff0c;也要内卷一下&#xff0c;感…

集合-List集合

系列文章目录 1.集合-Collection-CSDN博客​​​​​​ 2.集合-List集合-CSDN博客 文章目录 目录 系列文章目录 文章目录 前言 一 . 什么是List? 二 . List集合的特点 三 . 常用方法 1.void add(int index, E element): 将指定的元素插入到列表的指定位置。 2.E remove(int in…

1.4.C++项目:仿mudou库实现并发服务器之buffer模块的设计

一、buffer模块&#xff1a; 缓冲区模块 Buffer模块是一个缓冲区模块&#xff0c;用于实现通信中用户态的接收缓冲区和发送缓冲区功能。 二、提供的功能 存储数据&#xff0c;取出数据 三、实现思想 1.实现换出去得有一块内存空间&#xff0c;采用vector ,vector底层是一个…

opencv图像数组坐标系

在OpenCV的Python接口&#xff08;cv2&#xff09;中&#xff0c;加载的图像数组遵循以下坐标系和方向约定&#xff1a; 1. **坐标系&#xff1a;** OpenCV的坐标系遵循数学中的坐标系&#xff0c;原点&#xff08;0, 0&#xff09;位于图像的左上角。横轴&#xff08;X轴&…

算法通过村第十一关-位运算|青铜笔记|初始位运算

文章目录 前言1. 数字在计算中的表示拓展&#xff1a;为什么要有原码、反码和补码? 2. 位运算规则2.1 与、或、异或和取反2.2 位移运算2.3 位移运算和乘除的关系2.4 位运算的常用技巧 总结 前言 提示&#xff1a;我的父亲从我出生起便认识我&#xff0c;可他对我的了解却那么少…

自定义Unity组件——AudioManager(音频管理器)

需求描述 在游戏开发中&#xff0c;音频资源是不可或缺的&#xff0c;通常情况下音频资源随机分布&#xff0c;各个音频的操作和管理都是各自负责&#xff0c;同时对于音频的很多操作逻辑都是大同小异的&#xff0c;这就造成了许多冗余代码的堆叠&#xff0c;除此之外在获取各类…

ESP32S3的MPU-6050组件移植教程

前言 &#xff08;1&#xff09;实习公司要搞ESP32BOX的驱动移植&#xff0c;所有资料自己找还是比较折磨人的现在我分享几个官方的组件移植资料&#xff1a; <1>Find the most exciting ESP-IDF components&#xff08;ESP32的官方组件都可以在里面查&#xff0c;按照他…

【李沐深度学习笔记】损失函数

课程地址和说明 损失函数p2 本系列文章是我学习李沐老师深度学习系列课程的学习笔记&#xff0c;可能会对李沐老师上课没讲到的进行补充。 损失函数 损失函数是用来衡量预测值 y ^ \hat{y} y^​或 y ′ y y′与真实值 y y y的差别&#xff0c;下面给出常见的损失函数类型&am…

9.30号作业

1.消息队列实现进程间的通信 服务端 #include <myhead.h>#define ERR_MSG(msg) do{\fprintf(stderr,"__%d__:",__LINE__);\perror(msg);\ }while(0)typedef struct{ long msgtype; //消息类型char data[1024]; //消息正文 }Msg;#define SIZE sizeof(Msg)-s…

网站被上传webshell

1。原因 2.工具使用 3.步骤

1038 统计同成绩学生

输入样例&#xff1a; 10 60 75 90 55 75 99 82 90 75 50 3 75 90 88 输出样例&#xff1a; 3 2 0 solution #include <stdio.h> int main(){int n, d, k, hash[101] {0}, a[100000];scanf("%d", &n);for(int i 0; i < n; i){scanf("%d&quo…

SpringCloud网关服务

为什么需要网关 官网&#xff1a; https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.3.RELEASE/single/spring-cloud-gateway.html 使用 导入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>s…

第7讲:v-bind属性绑定,v-model双向绑定,v-on事件监听使用

目录 1.v-bind双向绑定 2.v-model双向绑定 3.v-on事件绑定 一.v-bind双向绑定 1.v-bind 指令可以在其名称后面带一个参数&#xff0c;中间放一个冒号隔开&#xff0c;这个参数通常是HTML元素的特性&#xff08;attribute&#xff09;&#xff0c;Vue官方提供了一个简写方式…