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

第14讲:深入理解指针(4)——函数指针与“程序跳转术”

🧭 第14讲:深入理解指针(4)——函数指针与“程序跳转术” 🔄🎯

指针进阶:掌握函数指针、回调机制与转移表的魔法!


📚 目录

  1. 字符指针变量:字符串的“只读守护者” 🔐
  2. 数组指针变量:指向数组的“导航仪” 🧭
  3. 二维数组传参的本质:行地址的传递 🔁
  4. 函数指针变量:指向函数的“遥控器” 🎮
  5. 函数指针数组:函数的“集合仓库” 🏭
  6. 转移表:用函数指针实现“智能调度” 🚦
  7. 学习收获总结 ✅

字符指针变量:字符串的“只读守护者” 🔐

🧩 基本用法

字符指针 char* 用于指向字符或字符串。

int main() {char ch = 'w';char *pc = &ch;*pc = 'w'; // ✅ 修改单个字符return 0;
}

🔒 指向字符串常量

int main() {const char* pstr = "hello bit."; // ✅ 推荐写法printf("%s\n", pstr);return 0;
}

📌 关键理解
"hello bit." 是一个字符串字面量,存储在只读内存区
pstr 存放的是首字符 'h' 的地址,即:pstr == &"hello bit."[0]

⚠️ 为什么用 const
防止通过指针修改只读内存,避免程序崩溃。


🧪 字符串常量的内存共享机制

#include <stdio.h>
int main() {char str1[] = "hello bit.";  // 栈上创建副本char str2[] = "hello bit.";  // 另一个副本const char *str3 = "hello bit."; // 指向常量区const char *str4 = "hello bit."; // 指向同一常量if(str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n"); // ✅ 输出if(str3 == str4)printf("str3 and str4 are same\n"); // ✅ 输出elseprintf("str3 and str4 are not same\n");return 0;
}

🌟 核心机制

  • str1, str2:数组初始化 → 各自开辟独立空间
  • str3, str4:指向字符串字面量 → 共享同一内存地址

结论
字符串字面量在程序运行期间只存储一份,多个指针可共享。


数组指针变量:指向数组的“导航仪” 🧭

🧩 什么是数组指针?

指针类型含义
int* p指向 int 变量
float* pf指向 float 变量
int (*p)[10]指向 int[10] 数组

📌 数组指针是“指针”,不是数组!
它存放的是整个数组的地址


🔍 如何区分指针数组与数组指针?

int *p1[10];     // ❌ 指针数组:数组,元素为 int*
int (*p2)[10];   // ✅ 数组指针:指针,指向 int[10]

🎯 记忆口诀

  • [] 优先级高于 *,所以 *p 必须加括号才能先结合
  • (*p) 表示 p 是指针,[10] 表示它指向一个10个int的数组

🔧 数组指针的初始化

int arr[10] = {0};
int (*p)[10] = &arr; // ✅ 获取整个数组的地址

📌 &arr 类型为 int(*)[10],与 p 类型完全匹配。


🧭 数组指针的类型解析

int (*p)[10] = &arr;
|   |     |
|   |     p指向数组的元素个数
|   p是数组指针变量名
p指向的数组的元素类型(int

二维数组传参的本质:行地址的传递 🔁

🔄 传统写法

void test(int a[3][5], int r, int c) {for(int i = 0; i < r; i++) {for(int j = 0; j < c; j++) {printf("%d ", a[i][j]);}printf("\n");}
}int main() {int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7}};test(arr, 3, 5);return 0;
}

🔍 二维数组的本质

  • 二维数组可视为“数组的数组”
  • arr[3][5] 是 3 个 int[5] 类型一维数组的集合
  • arr 是首元素地址 → 即第一行的地址
  • 第一行类型为 int[5] → 其地址类型为 int(*)[5]

✅ 指针形式传参

void test(int (*p)[5], int r, int c) {for(int i = 0; i < r; i++) {for(int j = 0; j < c; j++) {printf("%d ", *(*(p + i) + j)); // 等价于 p[i][j]}printf("\n");}
}

📌 p 是数组指针,指向 int[5] 类型的一维数组。


🎯 总结:二维数组传参

形参写法本质
int a[3][5]等价于 int (*a)[5]
int (*p)[5]显式数组指针

结论
二维数组传参,形参本质是指针,指向第一行(一维数组)。


函数指针变量:指向函数的“遥控器” 🎮

🧩 函数有地址吗?

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

结论
函数名就是函数的地址,test == &test


🔧 函数指针的声明

void (*pf1)() = &test;  // 指向无参无返回函数
void (*pf2)() = test;   // 省略 & 也可int Add(int x, int y) {return x + y;
}int (*pf3)(int, int) = Add;        // 指向 int(int,int) 函数
int (*pf4)(int x, int y) = &Add;   // 参数名可省略

🧭 函数指针类型解析

int (*pf3) (int x, int y)
|   |     -------------
|   |     |             |
|   |     pf3指向函数的参数类型和个数
|   函数指针变量名
pf3指向函数的返回类型(intint (*) (int, int)  // pf3 的完整类型

🔁 函数指针的使用

int main() {int (*pf)(int, int) = Add;printf("%d\n", (*pf)(2, 3)); // ✅ 解引用调用printf("%d\n", pf(3, 5));    // ✅ 直接调用(更常用)return 0;
}

📌 两种写法等价,pf(3,5) 更简洁。


🤯 两段“天书”代码解析

🔡 代码1:(void(*)())0)()
(*(void (*)())0)();

📖 解析

  1. void (*)():返回 void、无参的函数指针类型
  2. (void(*)())0:将地址 0 强制转为该函数指针
  3. *(...):解引用该指针
  4. (...):调用该函数

🚨 含义调用地址为0处的函数(常用于系统复位)


🔡 代码2:void (*signal(int, void(*)(int)))(int)
void (*signal(int num, void(*handler)(int)))(int);

📖 解析
signal 是一个函数,它:

  • 参数1:int num
  • 参数2:void(*handler)(int),一个函数指针
  • 返回值:void (*)(int),另一个函数指针

简化方法:使用 typedef

typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t); // 清晰明了!

函数指针数组:函数的“集合仓库” 🏭

🧩 什么是函数指针数组?

  • 指针数组int* arr[3] → 数组,元素为 int*
  • 函数指针数组:存放多个函数指针的数组
int (*parr[3])(); // ✅ 正确

📌 解析:

  • parr[3]:parr 是数组
  • int (*)():数组元素是函数指针,指向 int(void) 函数

❌ 常见错误写法

int *parr2[3]();   // ❌ 函数返回指针数组?
int (*)() parr3[3]; // ❌ 语法错误

转移表:用函数指针实现“智能调度” 🚦

🔄 传统计算器:switch 分支

// 使用 switch-case 实现加减乘除
// 代码冗长,不易扩展

🚀 转移表优化:函数指针数组

#include <stdio.h>int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return a / b; }int main() {int x, y, input, ret;// 转移表:函数指针数组int (*p[5])(int, int) = {0, add, sub, mul, div};do {printf("1:add  2:sub  3:mul  4:div  0:exit\n");printf("请选择:");scanf("%d", &input);if (input >= 1 && input <= 4) {printf("输入操作数:");scanf("%d %d", &x, &y);ret = p[input](x, y); // 直接调用printf("ret = %d\n", ret);} else if (input == 0) {printf("退出计算器\n");} else {printf("输入有误\n");}} while (input);return 0;
}

🎯 转移表的优势

优势说明
代码简洁替代冗长 switch
易于扩展新增功能只需添加函数和指针
高效调度O(1) 时间复杂度调用函数
回调基础qsort 等函数提供回调机制

🌟 应用场景

  • 操作系统中断处理
  • GUI 事件响应
  • 状态机设计
  • 高阶函数(如 qsort

✅ 学习收获总结

技能掌握情况
✅ 理解字符指针与字符串常量的关系✔️
✅ 区分指针数组与数组指针✔️
✅ 掌握二维数组传参的指针本质✔️
✅ 能声明并使用函数指针✔️
✅ 理解复杂函数指针声明(如 signal✔️
✅ 会使用 typedef 简化类型✔️
✅ 掌握函数指针数组与转移表应用✔️

🎯 函数指针是C语言实现“多态”与“高阶函数”的核心机制
你已掌握这一强大工具,继续前行,解锁更多系统级编程奥秘!💪🔥

💬 需要本讲的 函数指针练习题 / 转移表示例扩展 / qsort 预习资料?欢迎继续提问,我为你准备!

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

相关文章:

  • 湖北省建设网站首页公众平台网站开发哪家好
  • 重庆最有效的网站推广腾讯云搭建wordpress
  • x86、arm、rsc-v指令集架构,指令集、OS、应用3者的关系
  • 中科米堆CASAIM自动化三维测量实现注塑模具快速尺寸测量
  • ES6是什么
  • 课程网站开发 预算温州网络公司哪家最好
  • WebSocket 与 SSE 的区别,实际项目中应该怎么使用
  • 网站建设推广行业网站制作 江西
  • GPU 嗡嗡作响! Elastic 推理服务( EIS ):为 Elasticsearch 提供 GPU 加速推理
  • 前端碎碎念笔记:JavaScript 对象的封装与抽象
  • Spring Boot 3零基础教程,条件注解,笔记09
  • 余杭区住房与建设局网站wordpress目录权限
  • 认知觉醒 (一) · 感性
  • 谷歌站长平台承德市宽城县建设局网站
  • 【论文阅读】Sparks of Science
  • 论文笔记:π0.5 (PI 0.5)KI改进版
  • 【005】人个日程安排管理系统
  • 网站建设 北京做网站有必要用wordpress
  • 怎么做wp网站网上商城开发网站建设
  • Android Framework开机动画开发
  • 香港大学等提出增量天气预报模型VA-MoE,参数精简 75% 仍达 SOTA 性能
  • 北京企业建站服务中企论坛排名
  • 江门网站推广排名江苏省网站备案系统
  • Shuffle产生的三种场景
  • 公司网站制作的公司百度排名工具
  • 攻防世界-Web-shrine
  • arkTs:UIAbility 生命周期(补充版)
  • wordpress 首页导航代码广告网站建设网站排名优化
  • Synwit UI Creator中文输入法移植指南
  • wordpress tag生成的链接乱张家港优化网站seo