C语言的这两个概念,让无数面试者跪了

原创 无际单片机编程 2024-11-20 12:30

关注公众号,回复“入门资料”获取单片机入门到高级开挂教程

 开发板带你入门,我们带你飞

文 | 无际(微信:2777492857)

全文约2639字,阅读大约需要 10 分钟

上周我直播的时候,和我连麦的小哥说,他第一次面试时,面试官问了很多问题,他没回答出来。

其中一个问题,我觉得比较经典,而且尴尬的是,我也忘了。

就是函数指针和指针函数,有什么区别?

后面我大概查阅了一下资料,不就是指针和函数的区别而已嘛...

我和新手一样,很痛恨一些看起来复杂的专业名词,实际上就是纸老虎。

这就回到一个老生常谈的话题了,就是概念,代码之类的东西记不住咋办?

能记住,你就神了,这么多东西,靠记是不现实的。

徐工说,几个月前录过硬件基础的内容,现在也忘了一干二净了。

我以前写过的代码,有时也要静下心钻进去仔细研究,才能看明白。

我相信大多数工程师的记性都没这么好,特别是资深的,因为年纪越大,操心的屁事越多,哪里记得住。

这就是实际的情况,有句话叫熟能生巧,你可以记不住专业名词,但一定要有大量的实践,形成类似于肌肉记忆,这样印象才深刻,然后碰到类似的,你很快就能捡起来,或者举一反三。

我突然感觉面试,有点像学校考试,比如给你一张试卷做,或者问你一些概念性的东西,实际上,哪怕能说出来,可能能混过面试,但不能直接地证明你的动手能力。

下面回到主题,我将详细介绍下函数指针和指针函数的概念。

这两个概念,听起来很像,很容易混淆,我教你个技巧,只看后面两个字,比如指针,函数,就很好区分了。


一、基本概念

1.1 函数指针(Pointer to Function)

函数指针是一个指针,它指向函数的入口地址。

简单来说,就是用一个指针变量来保存函数的地址,通过这个指针可以间接地调用该函数。

如果是我们特训营学过项目3的老铁,应该非常熟悉了,我们大量回调函数的应用,就必须要用到函数指针。


1.2 指针函数(Function Returning Pointer)

指针函数本质是一个函数,只不过这个函数的返回值是一个指针,它返回一个特定类型的地址。


二、详细对比

2.1 函数指针的声明:

返回值类型 (*指针名)(参数列表);示例:int (*operation)(int, int);// 声明一个函数指针
实例:int (*operation)(int, int);
#include
// 定义两个普通函数int add(int a, int b) { return a + b;}
int subtract(int a, int b) { return a - b;}
int main() { // 声明一个函数指针 int (*operation)(int, int); // 指向add函数 operation = add; printf("加法结果:%d\n", operation(5, 3)); // 输出:8 // 指向subtract函数 operation = subtract; printf("减法结果:%d\n", operation(5, 3)); // 输出:2 return 0;}


输出结果:


2.2 指针函数的声明:

类型* 函数名(参数列表);
示例:int* createArray(int size) //指针函数:返回动态分配的整数数组{}
使用实例:#include #include
// 指针函数:返回动态分配的整数数组int* createArray(int size) { int* arr = (int*)malloc(size * sizeof(int)); for(int i = 0; i < size; i++) { arr[i] = i + 1; } return arr;}
int main() { int size = 5; int* numbers = createArray(size); for(int i = 0; i < size; i++) { printf("%d ", numbers[i]); // 输出:1 2 3 4 5 } free(numbers); return 0;}


输出结果:

三、实际应用场景

3.1 函数指针的应用

这里强调一下,函数指针是工程师进阶到架构师,必须要掌握的知识点,否则你的程序架构设计不会高级。

函数指针的应用也非常多,比如用函数指针做矩阵管理,我觉几个例子:


例子1:中断服务程序

一般单片机的中断向量表的实现,会采用函数指针,下面是代码模型,仅供参考。

// 中断向量表的实现typedef void (*ISR_Handler)(void);
ISR_Handler ISR_Vector[] = { NMI_Handler, // 不可屏蔽中断 HardFault_Handler, // 硬件错误中断 Timer0_Handler, // 定时器0中断 UART0_Handler, // 串口0中断 ADC0_Handler // ADC0中断};
// 动态修改中断处理函数void setTimerHandler(ISR_Handler newHandler) { ISR_Vector[2] = newHandler; // 修改定时器中断处理函数}

这行代码定义了一个函数指针类型ISR_Handler,用于指向中断服务函数。此类函数具有无返回值(void)、无参数(void)、专门用于中断服务程序的特征。

中断向量表包含多个中断服务函数的地址:

NMI_Handler:不可屏蔽中断处理函数

HardFault_Handler:硬件错误中断处理函数

Timer0_Handler:定时器0中断处理函数

UART0_Handler:串口0中断处理函数

ADC0_Handler:ADC0中断处理函数

setTimerHandler是一个可以动态修改中断处理函数的函数,原理是通过更新向量表中的函数指针实现。


这样做有什么好处?

主要体现在以下几个方面:

可维护性:中断向量表集中管理、结构清晰,易于维护、可根据不同场景切换处理函数。

可扩展性:比较模块化、增/减中断处理函数很方便。

类似的用法,还有很多别的应用场景,比如我们无际单片机的项目3:

LED控制:


按键检测:

这种方式,非常适合矩阵去管理控制方式相同,或者检测方式相同的场景。

比如一个产品有10个LED灯,每个LED都是高电平点亮,低电平熄灭。

比如一个产品有16个按键,每个按键低电平代表按下,高电平代表释放。

仔细领悟下,用函数指针数组的魅力。


例子2:回调函数示例:

#include 
// 回调函数类型typedef void (*CallBack)(int);
// 具体的回调函数实现void onSuccess(int result) { printf("操作成功,结果:%d\n", result);}
void onError(int errorCode) { printf("操作失败,错误码:%d\n", errorCode);}
// 执行操作的函数void processTask(int value, CallBack success, CallBack error) { if(value > 0) { success(value); } else { error(-1); }}
int main() { processTask(5, onSuccess, onError); // 成功场景 processTask(-5, onSuccess, onError); // 失败场景 return 0;}


输出结果:

这个例子,可能不能很好地体现回调函数的优势。

回调函数我觉得最屌的地方,就是我们能写出模块化代码的刚需,从专业术语就是耦合度低的代码,额...不说这万恶的专业术语了。

还是拿我们无际单片机的项目3举例吧。

随便举个按键检测的例子,从大体上我一般把程序分为两层:硬件层和应用层。

硬件层主要负责单片机按键相关引脚的配置,以及按键检测的逻辑代码,然后检测到具体的按键,具体的动作后,再把这个值传给应用层。

硬件层的按键检测代码如下:

代码有点长,篇幅有限,我只贴核心的部分。

KeyScanCBS是函数指针,keys是检测到按键具体的状态值,比如key1短按释放。

KeyScanCBS这个函数指针,我们会在应用层给这个函数指针注册。

这样就能在调用函数指针KeyScanCBS,就相当于调用了app.c文件的KeyEventHandle函数,就可以把keys的值从硬件层,传递给应用层app.c文件了。

如果这样看的有点懵,更详细的可以看我那套程序架构的视频,里面总结了很多实用的高阶编程技巧

这样费劲巴拉的,有啥好处?

就是硬件层和应用层之间的代码,独立程度更高了,不会共享全局变量啥的,移植性会更好,用专业术语来说就是模块化程度高,耦合性低。

还有函数指针用于形参的也挺多,比如我们项目6的OTA升级功能函数,形参就有用到。

反正,函数指针,必须要掌握!必须要掌握!必须要掌握!

面试能熟练描述我这里写的,相信能加分不少。


3.2 指针函数的应用

指针函数我用的不多,用起来不太习惯,这里就不详细介绍了,类似的应用有动态分配内存管理。

动态内存管理示例:

#include #include #include 
// 创建动态字符串char* createString(const char* str) { char* newStr = (char*)malloc(strlen(str) + 1); strcpy(newStr, str); return newStr;}
// 创建动态矩阵int** createMatrix(int rows, int cols) { int** matrix = (int**)malloc(rows * sizeof(int*)); for(int i = 0; i < rows; i++) { matrix[i] = (int*)malloc(cols * sizeof(int)); } return matrix;}
int main() { char* str = createString("Hello, World!"); printf("%s\n", str); free(str); int** matrix = createMatrix(3, 4); // 使用矩阵... // 释放内存 for(int i = 0; i < 3; i++) { free(matrix[i]); } free(matrix); return 0;}


四、总结下

4.1 本质区别

函数指针是一个指针变量,存储函数的地址。

指针函数是一个函数,返回值为指针类型。


4.2 使用场景

函数指针主要用于矩阵控制,回调机制等等。

指针函数主要用于动态内存分配、返回复杂数据结构等场景。


4.3 开发建议

掌握这两个知识点,非常重要,是你以后写出高灵活性、高扩展性,高移植性代码的刚需,学习方法是多实践应用。

祝你,代码越来越优雅高级。

end


下面是更多无际原创个人成长经历、行业经验、技术干货

1.电子工程师是怎样的成长之路?10年5000字总结

2.如何快速看懂别人的代码和思维

3.单片机开发项目全局变量太多怎么管理?

4.C语言开发单片机为什么大多数都采用全局变量的形式

5.单片机怎么实现模块化编程?实用程度让人发指!

6.c语言回调函数的使用及实际作用详解

7.手把手教你c语言队列实现代码,通俗易懂超详细!

8.c语言指针用法详解,通俗易懂超详细!

无际单片机编程 单片机编程、全栈孵化。
评论
  • 振动样品磁强计是一种用于测量材料磁性的精密仪器,广泛应用于科研、工业检测等领域。然而,其测量准确度会受到多种因素的影响,下面我们将逐一分析这些因素。一、温度因素温度是影响振动样品磁强计测量准确度的重要因素之一。随着温度的变化,材料的磁性也会发生变化,从而影响测量结果的准确性。因此,在进行磁性测量时,应确保恒温环境,以减少温度波动对测量结果的影响。二、样品制备样品的制备过程同样会影响振动样品磁强计的测量准确度。样品的形状、尺寸和表面处理等因素都会对测量结果产生影响。为了确保测量准确度,应严格按照规
    锦正茂科技 2025-02-28 14:05 134浏览
  • 应用趋势与客户需求,AI PC的未来展望随着人工智能(AI)技术的日益成熟,AI PC(人工智能个人电脑)逐渐成为消费者和企业工作中的重要工具。这类产品集成了最新的AI处理器,如NPU、CPU和GPU,并具备许多智能化功能,为用户带来更高效且直观的操作体验。AI PC的目标是提升工作和日常生活的效率,通过深度学习与自然语言处理等技术,实现更流畅的多任务处理、实时翻译、语音助手、图像生成等功能,满足现代用户对生产力和娱乐的双重需求。随着各行各业对数字转型需求的增长,AI PC也开始在各个领域中显示
    百佳泰测试实验室 2025-02-27 14:08 255浏览
  •           近日受某专业机构邀请,参加了官方举办的《广东省科技创新条例》宣讲会。在与会之前,作为一名技术工作者一直认为技术的法例都是保密和侵权方面的,而潜意识中感觉法律有束缚创新工作的进行可能。通过一个上午学习新法,对广东省的科技创新有了新的认识。广东是改革的前沿阵地,是科技创新的沃土,企业是创新的主要个体。《广东省科技创新条例》是广东省为促进科技创新、推动高质量发展而制定的地方性法规,主要内容包括: 总则:明确立法目
    广州铁金刚 2025-02-28 10:14 103浏览
  •         近日,广电计量在聚焦离子束(FIB)领域编写的专业著作《聚焦离子束:失效分析》正式出版,填补了国内聚焦离子束领域实践性专业书籍的空白,为该领域的技术发展与知识传播提供了重要助力。         随着芯片技术不断发展,芯片的集成度越来越高,结构也日益复杂。这使得传统的失效分析方法面临巨大挑战。FIB技术的出现,为芯片失效分析带来了新的解决方案。它能够在纳米尺度上对芯片进行精确加工和分析。当芯
    广电计量 2025-02-28 09:15 116浏览
  • 更多生命体征指标风靡的背后都只有一个原因:更多人将健康排在人生第一顺位!“AGEs,也就是晚期糖基化终末产物,英文名Advanced Glycation End-products,是存在于我们体内的一种代谢产物” 艾迈斯欧司朗亚太区健康监测高级市场经理王亚琴说道,“相信业内的朋友都会有关注,最近该指标的热度很高,它可以用来评估人的生活方式是否健康。”据悉,AGEs是可穿戴健康监测领域的一个“萌新”指标,近来备受关注。如果站在学术角度来理解它,那么AGEs是在非酶促条件下,蛋白质、氨基酸
    艾迈斯欧司朗 2025-02-27 14:50 400浏览
  • RGB灯光无法同步?细致的动态光效设定反而成为产品客诉来源!随着科技的进步和消费者需求变化,电脑接口设备单一功能性已无法满足市场需求,因此在产品上增加「动态光效」的形式便应运而生,藉此吸引消费者目光。这种RGB灯光效果,不仅能增强电脑周边产品的视觉吸引力,还能为用户提供个性化的体验,展现独特自我风格。如今,笔记本电脑、键盘、鼠标、鼠标垫、耳机、显示器等多种电脑接口设备多数已配备动态光效。这些设备的灯光效果会随着音乐节奏、游戏情节或使用者的设置而变化。想象一个画面,当一名游戏玩家,按下电源开关,整
    百佳泰测试实验室 2025-02-27 14:15 137浏览
  • 在物联网领域中,无线射频技术作为设备间通信的核心手段,已深度渗透工业自动化、智慧城市及智能家居等多元场景。然而,随着物联网设备接入规模的不断扩大,如何降低运维成本,提升通信数据的传输速度和响应时间,实现更广泛、更稳定的覆盖已成为当前亟待解决的系统性难题。SoC无线收发模块-RFM25A12在此背景下,华普微创新推出了一款高性能、远距离与高性价比的Sub-GHz无线SoC收发模块RFM25A12,旨在提升射频性能以满足行业中日益增长与复杂的设备互联需求。值得一提的是,RFM25A12还支持Wi-S
    华普微HOPERF 2025-02-28 09:06 143浏览
  • 1,微软下载免费Visual Studio Code2,安装C/C++插件,如果无法直接点击下载, 可以选择手动install from VSIX:ms-vscode.cpptools-1.23.6@win32-x64.vsix3,安装C/C++编译器MniGW (MinGW在 Windows 环境下提供类似于 Unix/Linux 环境下的开发工具,使开发者能够轻松地在 Windows 上编写和编译 C、C++ 等程序.)4,C/C++插件扩展设置中添加Include Path 5,
    黎查 2025-02-28 14:39 140浏览
  • 一、VSM的基本原理震动样品磁强计(Vibrating Sample Magnetometer,简称VSM)是一种灵敏且高效的磁性测量仪器。其基本工作原理是利用震动样品在探测线圈中引起的变化磁场来产生感应电压,这个感应电压与样品的磁矩成正比。因此,通过测量这个感应电压,我们就能够精确地确定样品的磁矩。在VSM中,被测量的样品通常被固定在一个震动头上,并以一定的频率和振幅震动。这种震动在探测线圈中引起了变化的磁通量,从而产生了一个交流电信号。这个信号的幅度和样品的磁矩有着直接的关系。因此,通过仔细
    锦正茂科技 2025-02-28 13:30 100浏览
  • 在2024年的科技征程中,具身智能的发展已成为全球关注的焦点。从实验室到现实应用,这一领域正以前所未有的速度推进,改写着人类与机器的互动边界。这一年,我们见证了具身智能技术的突破与变革,它不仅落地各行各业,带来新的机遇,更在深刻影响着我们的生活方式和思维方式。随着相关技术的飞速发展,具身智能不再仅仅是一个技术概念,更像是一把神奇的钥匙。身后的众多行业,无论愿意与否,都像是被卷入一场伟大变革浪潮中的船只,注定要被这股汹涌的力量重塑航向。01为什么是具身智能?为什么在中国?最近,中国具身智能行业的进
    艾迈斯欧司朗 2025-02-28 15:45 221浏览
  • 美国加州CEC能效跟DOE能效有什么区别?CEC/DOE是什么关系?美国加州CEC能效跟DOE能效有什么区别?CEC/DOE是什么关系?‌美国加州CEC能效认证与美国DOE能效认证在多个方面存在显著差异‌。认证范围和适用地区‌CEC能效认证‌:仅适用于在加利福尼亚州销售的电器产品。CEC认证的范围包括制冷设备、房间空调、中央空调、便携式空调、加热器、热水器、游泳池加热器、卫浴配件、光源、应急灯具、交通信号模块、灯具、洗碗机、洗衣机、干衣机、烹饪器具、电机和压缩机、变压器、外置电源、消费类电子设备
    张工nx808593 2025-02-27 18:04 120浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦