5

嵌入式软件架构设计-函数调用 - 大橙子疯

 1 year ago
source link: https://www.cnblogs.com/const-zpc/p/16364419.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

函数调用很好理解,即使刚学没多久的朋友也知道函数调用是怎么实现的,即调用一个已经封装好的函数,实现某个特定的功能。

把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口,然后在其他地方调用即可


2 函数调用方式

函数调用难道还能怎么调用?不就封装好直接调用吗???

函数调用方式分为两种:直接调用和间接调用

2.1 直接调用

直接调用就是我们平常使用的方式,下面的方式就属于直接调用了。

int SumFun(int a, int b)
{
    return a + b;
}

int main()
{
    // 直接调用定义好的函数
    int sum = SumFun(5, 6);
    printf("sum=%d", sum);
    return 0;
}

2.1 间接调用

间接调用在初学时很难使用到,这是通过函数指针的方式实现的。

函数指针本质是一个指针变量,是一个指向函数的指针(函数本身也是有地址的,指向的是函数入口);
指针函数本质是一个函数,其返回值为指针。

函数指针的用法如下:

typedef int (*FunctionCB)(int, int);

int SumFun(int a, int b)
{
    return a + b;
}

int main()
{
    // 将定义好的函数赋值给函数指针
    FunctionCB pfnSum = SumFun;

    // 通过函数指针间接调用
    int sum = pfnSum(5, 6);
    printf("sum=%d", sum);
    return 0;
}

3 什么场景下使用

函数指针在软件架构分层设计中十分重要,因为分层设计中有一个设计原则,那就是下层函数不能直接调用上层函数,那么可以通过函数指针的方式实现;一般称上层通过函数指针赋值给下层的函数为回调函数。

什么情况会存在需要下层程序需要调用上层程序的呢?
比如串口数据接收,虽然可以通过查询的方式接收,但是远不及通过串口中断的方式接收及时,当接收完成时,需要立即通知上层读取数据进行处理,而不是等待上层程序查询读取。

如何实现呢?
比如硬件抽象层/驱动层中的串口模块实现函数

/************* UART.c 文件 ****************/
static UartRecvCB sg_pfnUartRecv;

// 设置数据帧接收处理回调函数
void UART_SetRecvCallback(UartRecvCB pfnUartRecv)
{
    sg_pfnUartRecv = pfnUartRecv;
}

void UART_Task(void)
{
    if (RecvEnd)
    {
        // 数据一帧接收完成立即调用
        if (sg_pfnUartRecv != NULL)
        {
            sg_pfnUartRecv(UartRecvBuf, UartRecvLength);
        }
    }
}

/************* UART.h 文件 ****************/
typedef void (*UartRecvCB)(const char *, int);

extern void UART_SetRecvCallback(UartRecvCB pfnUartRecv);
extern void UART_Task(void);

应用层代码中实现回调函数,并调用下层函数。

// 回调函数:串口数据处理
void OnUartRecvProcess(const char *pBuf, int length)
{
    // 处理串口数据
    printf("Recv: %s", pBuf);
}

int main()
{
    UART_SetRecvCallback(OnUartRecvProcess);

    while(1)
    {
        if (TimeFlag)
        {
            UART_Task();
        }
    }
}

上述示例中通过函数指针的方式间接调用了应用层的函数,而且并不违背分层设计原则。
如果看代码不能立即理解的话,可以尝试通过下图理解:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aSn5qmZ5a2Q55av,size_20,color_FFFFFF,t_70,g_se,x_16

 Callback 是一个函数指针类型的变量,通过函数 FML_TIME_Attach 拿到了应用层代码函数 OnFunction(...) 的函数地址,之后在定时器中断函数中根据触发条件调用 Callback 即可,调用方式和直接调用 OnFunction(...) 没有太大差异,只不过名字不一样(可以理解成取了一个别名),为了保证系统运行安全,调用前要确保  Callback 不为 NULL,否则会引起程序异常。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK