【C语言进阶】C语言指针进阶实战:优化与难题解析

2024-08-29 03:36

本文主要是介绍【C语言进阶】C语言指针进阶实战:优化与难题解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

📝个人主页🌹:Eternity._
⏩收录专栏⏪:C语言 “ 登神长阶 ”
🤡往期回顾🤡:C语言指针进阶 (上)
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

❀C语言指针进阶

  • 📒1. 函数指针
  • 📙2. 函数指针数组
  • 📚3. 指向函数指针数组的指针
  • 📜4. 回调函数
  • 📝5. 指针笔试题
  • 📖6. 总结


前言:在C语言的浩瀚宇宙中,指针无疑是那颗最为璀璨而神秘的星辰。它既是连接数据与操作的桥梁,也是让许多初学者望而生畏的迷宫。一旦掌握了指针的精髓,C语言的世界便仿佛被彻底点亮,编程的效率和灵活性得以质的飞跃。然而,仅仅停留在指针的基本使用上,远不足以探索其全部魅力与力量。指针的进阶应用,尤其是如何通过指针优化程序性能、解决复杂难题,是每一位C语言开发者必须攀登的高峰

每个实战案例都将配以详细的代码示例与解释,旨在让读者不仅能够理解其背后的原理,更能够亲手实践,将所学知识转化为解决问题的能力。同时,我们也会对一些常见的指针难题进行深度解析,比如指针运算的陷阱、多级指针的理解难点等,帮助读者彻底克服这些学习障碍

让我们继续一同踏上这场充满挑战与收获的指针进阶之旅吧!


📒1. 函数指针

C语言中的函数指针是一种特殊的指针类型,它存储的不是变量的地址,而是函数的地址。通过函数指针,我们可以在运行时动态地调用函数,这增加了程序的灵活性和模块化。函数指针在回调函数、中断服务例程、以及实现函数表(如多态)等场景中非常有用

void test()
{printf("hehe\n");
}int main()
{printf("%p\n", test);printf("%p\n", &test);return 0;
}

在这里插入图片描述

输出的是两个地址,这两个地址是 test 函数的地址,那么我们怎么保存函数的地址呢?

定义:

// 保存函数的地址
void (*pfun1)();

解析:pfun1可以存放存储地址。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void


我们再来看一下这两个代码

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);

代码1:

这段代码尝试执行一个位于地址0处的函数。首先,(void (*)())0是一个类型转换,它将整数0转换为指向返回类型为void且不接受任何参数的函数的指针。然后,*操作符对转换后的指针进行解引用,尝试获取该函数指针指向的函数。最后,()用于调用该函数

代码2:

  • signal是一个函数,它接受两个参数:
    • 第一个参数是int类型,通常用于指定要处理的信号编号。
    • 第二个参数是一个指向函数的指针,这个函数接受一个int参数(通常是信号编号)并返回void。
  • signal函数的返回类型是一个指向函数的指针,这个函数也接受一个int参数并返回void

📙2. 函数指针数组

把函数的地址存到一个数组中,那这个数组就叫函数指针数组

定义:

int (*parr1[10])();
// parr1 先和 [] 结合,说明 parr1是数组
// 存放int (*)() 类型的函数指针

举个例子来展示如何定义一个函数指针数组,并向其中添加函数指针,最后通过索引来调用这些函数

// 定义三个示例函数  
void func1(int x) 
{printf("Func1 called with %d\n", x);
}void func2(int x) 
{printf("Func2 called with %d\n", x);
}void func3(int x) 
{printf("Func3 called with %d\n", x);
}// 定义一个指向函数的指针类型,该函数接受一个int参数并返回void  
typedef void (*FuncPtr)(int);int main() {// 创建一个函数指针数组,可以存储三个指向函数的指针  FuncPtr funcArr[3] = { func1, func2, func3 };// 通过索引调用函数  for (int i = 0; i < 3; i++) {funcArr[i](i + 1); // 调用函数,并将索引值+1作为参数传递  }return 0;
}

📚3. 指向函数指针数组的指针

指向函数指针数组的指针是一个 指针
指针指向一个 数组 ,数组的元素都是 函数指针

定义:

void test(const char* str)
{printf("%s\n", str);
}int main()
{//函数指针pfunvoid (*pfun)(const char*) = test;//函数指针的数组pfunArrvoid (*pfunArr[5])(const char* str);pfunArr[0] = test;//指向函数指针数组pfunArr的指针ppfunArrvoid (*(*ppfunArr)[5])(const char*) = &pfunArr;return 0;
}

📜4. 回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应

// 定义一个回调函数的类型,该函数接受一个int参数并返回void  
typedef void (*CallbackFunc)(int);// 一个需要回调函数的函数  
void processData(int data, CallbackFunc callback) 
{printf("Processing data: %d\n", data);// 调用回调函数  callback(data);
}// 用户定义的回调函数  
void myCallback(int data) {printf("Callback called with data: %d\n", data);
}int main() {// 调用需要回调函数的函数,并传递用户定义的回调函数作为参数  processData(10, myCallback);return 0;
}

processData函数接受一个整数和一个回调函数作为参数。在processData函数内部,首先执行一些处理,然后调用回调函数callback,并将之前接收到的整数data作为参数传递给回调函数。用户定义的回调函数myCallback被传递给processData函数,并在适当的时候被调用

回调函数广泛应用于事件处理、排序算法(如快速排序中的比较函数)


📝5. 指针笔试题

题目一:

int main()
{int a[5] = { 1, 2, 3, 4, 5 };int* ptr = (int*)(&a + 1);printf("%d,%d", *(a + 1), *(ptr - 1));return 0;
}

在这里插入图片描述


题目二:

int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };int* p;p = a[0];printf("%d", p[0]);return 0;
}

数组初始化时使用的 (0, 1) 形式的初始化实际上是逗号运算符(comma operator)的使用。逗号运算符会评估其两个操作数,但只返回最后一个操作数的值

这里 p 被赋值为 a[0] 的地址,即 &a[0][0]。然后 p[0] 访问了这个地址的内容,即 a[0][0],其值为1
在这里插入图片描述


题目三:

int main()
{char* a[] = { "work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa);return 0;
}

这道题目比较简单,各位可以自己看看


题目四:

//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a)); // 16 数组大小
printf("%d\n",sizeof(a+0)); // 4 数组元素的大小
printf("%d\n",sizeof(*a)); // 4 首元素大小
printf("%d\n",sizeof(a+1)); // 4 数组元素的大小
printf("%d\n",sizeof(a[1])); // 4 数组元素的大小
printf("%d\n",sizeof(&a)); // 4 数组首元素的大小
printf("%d\n",sizeof(*&a)); // 16 数组大小
printf("%d\n",sizeof(&a+1)); // 4 数组元素的大小
printf("%d\n",sizeof(&a[0])); // 4 数组元素的大小
printf("%d\n",sizeof(&a[0]+1)); // 4 数组元素的大小

📖6. 总结

随着指针进阶学习的深入,我们仿佛揭开了C语言世界中最为神秘而又引人入胜的一角。在上一部分,我们已经见证了指针如何成为连接数据与内存的桥梁,而在这部分,我们更是将这份力量发挥到了极致

回顾这段学习之旅,我们不难发现,指针不仅是C语言的核心特性之一,更是编程世界中一把强大的双刃剑。它既能让我们实现高效的内存管理与复杂的数据操作,也可能因不当使用而引发难以察觉的错误与漏洞。因此,在享受指针带来的便利与乐趣的同时,我们也必须时刻保持警惕,遵循最佳实践,确保代码的安全与可靠

至此,我们的C语言指针进阶之旅即将画上圆满的句号。但请记住,学习永无止境,技术的海洋浩瀚无垠。愿每一位编程爱好者都能保持对技术的热爱与追求,继续在编程的道路上探索前行,创造属于自己的辉煌篇章

在这里插入图片描述

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

在这里插入图片描述

这篇关于【C语言进阶】C语言指针进阶实战:优化与难题解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1116729

相关文章

全面解析HTML5中Checkbox标签

《全面解析HTML5中Checkbox标签》Checkbox是HTML5中非常重要的表单元素之一,通过合理使用其属性和样式自定义方法,可以为用户提供丰富多样的交互体验,这篇文章给大家介绍HTML5中C... 在html5中,Checkbox(复选框)是一种常用的表单元素,允许用户在一组选项中选择多个项目。本

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python包管理工具核心指令uvx举例详细解析

《Python包管理工具核心指令uvx举例详细解析》:本文主要介绍Python包管理工具核心指令uvx的相关资料,uvx是uv工具链中用于临时运行Python命令行工具的高效执行器,依托Rust实... 目录一、uvx 的定位与核心功能二、uvx 的典型应用场景三、uvx 与传统工具对比四、uvx 的技术实

SpringBoot排查和解决JSON解析错误(400 Bad Request)的方法

《SpringBoot排查和解决JSON解析错误(400BadRequest)的方法》在开发SpringBootRESTfulAPI时,客户端与服务端的数据交互通常使用JSON格式,然而,JSON... 目录问题背景1. 问题描述2. 错误分析解决方案1. 手动重新输入jsON2. 使用工具清理JSON3.

从基础到进阶详解Pandas时间数据处理指南

《从基础到进阶详解Pandas时间数据处理指南》Pandas构建了完整的时间数据处理生态,核心由四个基础类构成,Timestamp,DatetimeIndex,Period和Timedelta,下面我... 目录1. 时间数据类型与基础操作1.1 核心时间对象体系1.2 时间数据生成技巧2. 时间索引与数据

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

Go语言如何判断两张图片的相似度

《Go语言如何判断两张图片的相似度》这篇文章主要为大家详细介绍了Go语言如何中实现判断两张图片的相似度的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 在介绍技术细节前,我们先来看看图片对比在哪些场景下可以用得到:图片去重:自动删除重复图片,为存储空间"瘦身"。想象你是一个

Go语言中Recover机制的使用

《Go语言中Recover机制的使用》Go语言的recover机制通过defer函数捕获panic,实现异常恢复与程序稳定性,具有一定的参考价值,感兴趣的可以了解一下... 目录引言Recover 的基本概念基本代码示例简单的 Recover 示例嵌套函数中的 Recover项目场景中的应用Web 服务器中