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语言指针用法详解,通俗易懂超详细!

无际单片机编程 单片机编程、全栈孵化。
评论
  • 彼得·德鲁克被誉为“现代管理学之父”,他的管理思想影响了无数企业和管理者。然而,关于他的书籍分类,一种流行的说法令人感到困惑:德鲁克一生写了39本书,其中15本是关于管理的,而其中“专门写工商企业或为企业管理者写的”只有两本——《为成果而管理》和《创新与企业家精神》。这样的表述广为流传,但深入探讨后却发现并不完全准确。让我们一起重新审视这一说法,解析其中的矛盾与根源,进而重新认识德鲁克的管理思想及其著作的真正价值。从《创新与企业家精神》看德鲁克的视角《创新与企业家精神》通常被认为是一本专为企业管
    优思学院 2025-01-06 12:03 90浏览
  • 随着市场需求不断的变化,各行各业对CPU的要求越来越高,特别是近几年流行的 AIOT,为了有更好的用户体验,CPU的算力就要求更高了。今天为大家推荐由米尔基于瑞芯微RK3576处理器推出的MYC-LR3576核心板及开发板。关于RK3576处理器国产CPU,是这些年的骄傲,华为手机全国产化,国人一片呼声,再也不用卡脖子了。RK3576处理器,就是一款由国产是厂商瑞芯微,今年第二季推出的全新通用型的高性能SOC芯片,这款CPU到底有多么的高性能,下面看看它的几个特性:8核心6 TOPS超强算力双千
    米尔电子嵌入式 2025-01-03 17:04 51浏览
  • 大模型的赋能是指利用大型机器学习模型(如深度学习模型)来增强或改进各种应用和服务。这种技术在许多领域都显示出了巨大的潜力,包括但不限于以下几个方面: 1. 企业服务:大模型可以用于构建智能客服系统、知识库问答系统等,提升企业的服务质量和运营效率。 2. 教育服务:在教育领域,大模型被应用于个性化学习、智能辅导、作业批改等,帮助教师减轻工作负担,提高教学质量。 3. 工业智能化:大模型有助于解决工业领域的复杂性和不确定性问题,尽管在认知能力方面尚未完全具备专家级的复杂决策能力。 4. 消费
    丙丁先生 2025-01-07 09:25 36浏览
  • 在快速发展的能源领域,发电厂是发电的支柱,效率和安全性至关重要。在这种背景下,国产数字隔离器已成为现代化和优化发电厂运营的重要组成部分。本文探讨了这些设备在提高性能方面的重要性,同时展示了中国在生产可靠且具有成本效益的数字隔离器方面的进步。什么是数字隔离器?数字隔离器充当屏障,在电气上将系统的不同部分隔离开来,同时允许无缝数据传输。在发电厂中,它们保护敏感的控制电路免受高压尖峰的影响,确保准确的信号处理,并在恶劣条件下保持系统完整性。中国国产数字隔离器经历了重大创新,在许多方面达到甚至超过了全球
    克里雅半导体科技 2025-01-03 16:10 122浏览
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 91浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 100浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 111浏览
  • 本文介绍Linux系统更换开机logo方法教程,通用RK3566、RK3568、RK3588、RK3576等开发板,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。制作图片开机logo图片制作注意事项(1)图片必须为bmp格式;(2)图片大小不能大于4MB;(3)BMP位深最大是32,建议设置为8;(4)图片名称为logo.bmp和logo_kernel.bmp;开机
    Industio_触觉智能 2025-01-06 10:43 84浏览
  • PLC组态方式主要有三种,每种都有其独特的特点和适用场景。下面来简单说说: 1. 硬件组态   定义:硬件组态指的是选择适合的PLC型号、I/O模块、通信模块等硬件组件,并按照实际需求进行连接和配置。    灵活性:这种方式允许用户根据项目需求自由搭配硬件组件,具有较高的灵活性。    成本:可能需要额外的硬件购买成本,适用于对系统性能和扩展性有较高要求的场合。 2. 软件组态   定义:软件组态主要是通过PLC
    丙丁先生 2025-01-06 09:23 71浏览
  •     为控制片内设备并且查询其工作状态,MCU内部总是有一组特殊功能寄存器(SFR,Special Function Register)。    使用Eclipse环境调试MCU程序时,可以利用 Peripheral Registers Viewer来查看SFR。这个小工具是怎样知道某个型号的MCU有怎样的寄存器定义呢?它使用一种描述性的文本文件——SVD文件。这个文件存储在下面红色字体的路径下。    例:南京沁恒  &n
    电子知识打边炉 2025-01-04 20:04 89浏览
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 169浏览
  • 光耦合器,也称为光隔离器,是一种利用光在两个隔离电路之间传输电信号的组件。在医疗领域,确保患者安全和设备可靠性至关重要。在众多有助于医疗设备安全性和效率的组件中,光耦合器起着至关重要的作用。这些紧凑型设备经常被忽视,但对于隔离高压和防止敏感医疗设备中的电气危害却是必不可少的。本文深入探讨了光耦合器的功能、其在医疗应用中的重要性以及其实际使用示例。什么是光耦合器?它通常由以下部分组成:LED(发光二极管):将电信号转换为光。光电探测器(例如光电晶体管):检测光并将其转换回电信号。这种布置确保输入和
    腾恩科技-彭工 2025-01-03 16:27 174浏览
  • 物联网(IoT)的快速发展彻底改变了从智能家居到工业自动化等各个行业。由于物联网系统需要高效、可靠且紧凑的组件来处理众多传感器、执行器和通信设备,国产固态继电器(SSR)已成为满足中国这些需求的关键解决方案。本文探讨了国产SSR如何满足物联网应用的需求,重点介绍了它们的优势、技术能力以及在现实场景中的应用。了解物联网中的固态继电器固态继电器是一种电子开关设备,它使用半导体而不是机械触点来控制负载。与传统的机械继电器不同,固态继电器具有以下优势:快速切换:确保精确快速的响应,这对于实时物联网系统至
    克里雅半导体科技 2025-01-03 16:11 181浏览
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 121浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦