电子产业一站式赋能平台

PCB联盟网

搜索
查看: 22|回复: 0
收起左侧

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

[复制链接]

269

主题

269

帖子

1498

积分

三级会员

Rank: 3Rank: 3

积分
1498
发表于 昨天 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
    ", operation(5, 3));  // 输出:8        // 指向subtract函数    operation = subtract;    printf("减法结果:%d
    ", operation(5, 3));  // 输出:2        return 0;}
    输出结果:

    mb1htv1bc126401686325.png

    mb1htv1bc126401686325.png



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

    v4elihyace26401686425.png

    v4elihyace26401686425.png

    三、实际应用场景
    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控制:

    ldpjna4oaj36401686525.png

    ldpjna4oaj36401686525.png



    按键检测:

    sx413iw2cuq6401686625.png

    sx413iw2cuq6401686625.png

    这种方式,非常适合矩阵去管理控制方式相同,或者检测方式相同的场景。
    比如一个产品有10个LED灯,每个LED都是高电平点亮,低电平熄灭。
    比如一个产品有16个按键,每个按键低电平代表按下,高电平代表释放。
    仔细领悟下,用函数指针数组的魅力。

    例子2:回调函数示例:
  • #include
    // 回调函数类型typedef void (*CallBack)(int);
    // 具体的回调函数实现void onSuccess(int result) {    printf("操作成功,结果:%d
    ", result);}
    void onError(int errorCode) {    printf("操作失败,错误码:%d
    ", 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;}

    输出结果:

    nkk52gjtj3d6401686725.png

    nkk52gjtj3d6401686725.png

    这个例子,可能不能很好地体现回调函数的优势。
    回调函数我觉得最屌的地方,就是我们能写出模块化代码的刚需,从专业术语就是耦合度低的代码,额...不说这万恶的专业术语了。
    还是拿我们无际单片机的项目3举例吧。
    随便举个按键检测的例子,从大体上我一般把程序分为两层:硬件层和应用层。
    硬件层主要负责单片机按键相关引脚的配置,以及按键检测的逻辑代码,然后检测到具体的按键,具体的动作后,再把这个值传给应用层。
    硬件层的按键检测代码如下:

    l0aqh3vv0o26401686826.png

    l0aqh3vv0o26401686826.png

    代码有点长,篇幅有限,我只贴核心的部分。
    KeyScanCBS是函数指针,keys是检测到按键具体的状态值,比如key1短按释放。
    KeyScanCBS这个函数指针,我们会在应用层给这个函数指针注册。

    mclcwbmva3y6401686926.png

    mclcwbmva3y6401686926.png

    这样就能在调用函数指针KeyScanCBS,就相当于调用了app.c文件的KeyEventHandle函数,就可以把keys的值从硬件层,传递给应用层app.c文件了。
    如果这样看的有点懵,更详细的可以看我那套程序架构的视频,里面总结了很多实用的高阶编程技巧

    rhcezys0uva6401687026.png

    rhcezys0uva6401687026.png

    这样费劲巴拉的,有啥好处?
    就是硬件层和应用层之间的代码,独立程度更高了,不会共享全局变量啥的,移植性会更好,用专业术语来说就是模块化程度高,耦合性低。
    还有函数指针用于形参的也挺多,比如我们项目6的OTA升级功能函数,形参就有用到。

    btxjyvl2xf36401687126.png

    btxjyvl2xf36401687126.png

    反正,函数指针,必须要掌握!必须要掌握!必须要掌握!
    面试能熟练描述我这里写的,相信能加分不少。

    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         matrix = (int*)malloc(cols * sizeof(int));    }    return matrix;}
    int main() {    char* str = createString("Hello, World!");    printf("%s
    ", str);    free(str);        int** matrix = createMatrix(3, 4);    // 使用矩阵...    // 释放内存    for(int i = 0; i 3; i++) {        free(matrix);    }    free(matrix);        return 0;}

    四、总结下
    4.1 本质区别
    函数指针是一个指针变量,存储函数的地址。
    指针函数是一个函数,返回值为指针类型。

    4.2 使用场景
    函数指针主要用于矩阵控制,回调机制等等。
    指针函数主要用于动态内存分配、返回复杂数据结构等场景。

    4.3 开发建议
    掌握这两个知识点,非常重要,是你以后写出高灵活性、高扩展性,高移植性代码的刚需,学习方法是多实践应用。
    祝你,代码越来越优雅高级。
    end

    k1ddnmegbz46401687226.jpg

    k1ddnmegbz46401687226.jpg

    下面是更多无际原创的个人成长经历、行业经验、技术干货。
    1.电子工程师是怎样的成长之路?10年5000字总结
    2.如何快速看懂别人的代码和思维
    3.单片机开发项目全局变量太多怎么管理?
    4.C语言开发单片机为什么大多数都采用全局变量的形式
    5.单片机怎么实现模块化编程?实用程度让人发指!
    6.c语言回调函数的使用及实际作用详解

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

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

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表