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

15-C语言:第15~16天笔记

函数指针

定义

函数指针本质上是指针,是一个指向函数的指针。函数都有一个入口地址,所谓指向函数

的指针,就是指向函数的入口地址。(这里的函数名就代表入口地址)

函数指针存在的意义:

  • 让函数多了一种调用方式
  • 函数指针可以作为形参,可以形式调用(回调函数)

遵循:先有函数,后有指针

语法:

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

举例:

int (*fun)(int a, int b);int (*fun)(int, int); // 有点类似于函数声明

函数指针的初始化

① 定义的同时赋值

// 函数指针需要依赖于函数,先有函数,后有指针
// 定义一个普通函数
int add(int a, int b) { return a + b; }
// 定义一个函数指针,并初始化
// 观察:函数指针的返回类型和指向的函数的返回类型一致,函数指针的形参个数、类
型、位置和指向的函数的参数的一致。
int (*p)(int a, int b) = add; // 函数指针p指向函数add,这里的add不能带(),
add就表示函数的入口地址

② 先定义,后赋值

// 定义一个普通函数int add(int a, int b) { return a + b; }int (*p)(int, int); // 形参列表的参数名可以省略不写p = add; // 此时是将add的入口地址赋值给指针p

注意:

  1. 函数指针指向的函数要和函数指针定义的返回值类型,形参列表对应,否则编

译报错

  1. 函数指针是指针,但不能指针运算,如p++等,没有实际意义
  2. 函数指针作为形参,可以形成回调
  3. 函数指针作为形参,函数调用时的实参只能是与之对应的函数名,不能带小括

号()

  1. 函数指针的形参列表中的变量名可以省略

注意:函数不能作为函数的形参,但是指向函数的函数指针是可以作为函数的形参的。

案例

需求:求a,b两个数的最大值

代码:

#include <stdio.h>/**\* 定义一个函数,求两个数的最大值*/int get_max(int a, int b){return a > b ? a : b;}int main(int argc,char *argv[]){// 定义测试数据int a = 3, b = 4, max;// 直接调用函数max = get_max(a, b);printf("%d,%d中的最大值是:%d\n",a,b,max);// 定义一个函数指针int (*p)(int, int) = get_max;// 间接调用函数:方式1max = p(a,b);printf("%d,%d中的最大值是:%d\n",a,b,max);// 间接调用函数:方式2max = (*p)(a,b); // () > [] > *printf("%d,%d中的最大值是:%d\n",a,b,max);return 0;
}

回调函数

定义

	==回调函数就是一个通过函数指针调用的函数==。如果你把函数的指针作为参数传递给另

一个函数,当这个指针被用来调用其所指向的函数时。我们就说这是回调函数。回调函数

不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,

用于对该事件或条件进行响应。

​ 简单来说,就是使用函数指针作为函数的形参,这种函数就被称作回调函数。

为什么要用回调函数

​ 因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存

在一个具有特定原型和限制条件的被调用函数。

​ 简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函

数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。

案例

#include <stdio.h>
/**
\* 回调函数1*/int callback_1(int a){printf("hello, this is callback_1:a=%d\n", a);return a;}/**\* 回调函数2*/int callback_2(int b){printf("hello, this is callback_2:b=%d\n", b);return b;}/**\* 回调函数3*/int callback_3(int c){printf("hello, this is callback_3:c=%d\n", c);return c;}/**\* 实现回调函数(形参是函数指针的函数)*/int handle(int x, int (*callback)(int)){printf("日志:开始执行任务!\n");int res = callback(x);printf("日志:执行结果:%d\n",res);printf("日志:结束执行任务!\n");}int main(int argc,char *argv[]){handle(100,callback_1);handle(200,callback_2);handle(300,callback_3);return 0;}

运行结果:

在这里插入图片描述

二级指针

定义

​ 二级指针(多重指针)用于储存一级指针的地址,需要两次解引用才能访问原始数据。其

他多重指针的用法类似,但实际开发中最常见的指针是二级指针。

int a = 10; // a是普通变量,也就是原始数据
int *p = &a; // 一级指针,p指向a,解引用1次就可以获取a的值
printf("%d\n", *p); // 10int **w = &p; // 二级指针,w指向p,解引用2次就可以获取a的值
printf("%d\n", **w);// 10int ***k = &w; // 三级指针,k指向w,解引用3次就可以获取a的值
printf("%d\n", ***k); // 10 int a1 = ***k; int *a2 = **k; int **a3
= *k; int ***a4 = k;
语法

数据类型 **指针变量名 = 指针数组的数组名 | 一级指针的地址

特点

与指针数组的等效性 二级指针与指针数组在某些时候存在等效性,但与二维数组不等

效。二维数组名是数组指针类型,如 int (*)[3] ,而非二级指针。

// 指针数组
int arr[] = {11,22,33};
int *arr_[] = {&arr[0],&arr[1],&arr[2]};
// 二级指针接收指针数组
char *str[3] = {"abc","aaa034","12a12"};
char **p = str; // p:数组首地址,行地址,默认0行 *p:列地址,默认0行0列
**p:列元素
#include <stdio.h>
int main(int argc,char *argv[])
{char *str[3] = {"abc","aaa034","12a12"};char **p = str;// 打印字符串// for (int i = 0; i < 3; i++)// {// printf("%s\n", *p);// p++;// }// 打印字符int i = 0;while(**p != '\0'){printf("%-2c",**p);(*p)++;}printf("\n");return 0;
}

与二维数组的差异 二维数组名是数组指针类型,直接赋值给二级指针会导致类型不匹

// 数组指针可以指向一个二维数组
int arr[2][3] = {{1,3,5},{11,33,55}};
int (*p)[3] = arr;
// 二级指针不等效二维数组
int **k = arr; // 编译报错 arr类型 int(*)[3] 不兼容 k类型 int**

0’)
{
printf(“%-2c”,**p);
(*p)++;
}
printf(“\n”);
return 0;
}


② **与二维数组的差异** 二维数组名是数组指针类型,直接赋值给二级指针会导致类型不匹配

// 数组指针可以指向一个二维数组
int arr[2][3] = {{1,3,5},{11,33,55}};
int (p)[3] = arr;
// 二级指针不等效二维数组
int **k = arr; // 编译报错 arr类型 int(
)[3] 不兼容 k类型 int**

http://www.lryc.cn/news/604204.html

相关文章:

  • dubbo应用之3.0新特性(响应式编程)(2)
  • 《剑指offer》-算法篇-位运算
  • window weblogic 解锁
  • Object.freeze() 深度解析:不可变性的实现与实战指南
  • 第4章唯一ID生成器——4.5 美团点评开源方案Leaf
  • JVM易混淆名称
  • 【24】C# 窗体应用WinForm ——日历MonthCalendar属性、方法、实例应用
  • 在依赖关系正确的情况下,执行 mvn install 提示找不到软件包
  • 测试自动化不踩坑:4 策略告别 “为自动化而自动化”
  • DPDK PMD 深度解析:高性能网络的核心引擎
  • 使用LangChain构建法庭预定智能体:结合vLLM部署的Qwen3-32B模型
  • 疯狂星期四文案网第23天运营日记
  • 基于Matlab图像处理的静态雨滴去除与质量评估系统
  • 数学建模算法-day[14]
  • LeetCode 刷题【19. 删除链表的倒数第 N 个结点、20. 有效的括号、21. 合并两个有序链表】
  • 面试刷题平台项目总结
  • 用命令查看Android设备的 Linux 内核版本,了解其对应的硬件支持各种特性
  • Git命令保姆级教程
  • 如何进行项目复盘?核心要点分析
  • AI产品经理手册(Ch3-5)AI Product Manager‘s Handbook学习笔记
  • linux命令tail的实际应用
  • C语言---万能指针(void *)、查找子串(strncmp函数的应用)多维数组(一维数组指针、二维数组指针)、返回指针值函数、关键字(const)
  • 【RH134 问答题】第 9 章 访问网络附加存储
  • 什么是数据编排?数据编排的流程、优势、挑战及工具有哪些?
  • OpenLayers 综合案例-底图换肤(变色)
  • Intellij Idea--解决Cannot download “https://start.spring.io‘: Connect timedout
  • 前端路由
  • DAY21 常见的降维算法
  • vulhub 02-Breakout靶场攻略
  • 计算机网络基础(一) --- (网络通信三要素)