嵌入式軟體架構設計-函數調用

来源:https://www.cnblogs.com/const-zpc/archive/2022/06/10/16364419.html
-Advertisement-
Play Games

1 前言 函數調用很好理解,即使剛學沒多久的朋友也知道函數調用是怎麼實現的,即調用一個已經封裝好的函數,實現某個特定的功能。 把一個或者多個功能通過函數的方式封裝起來,對外只提供一個簡單的函數介面,然後在其他地方調用即可 2 函數調用方式 函數調用難道還能怎麼調用?不就封裝好直接調用嗎??? 函數調 ...


1 前言

函數調用很好理解,即使剛學沒多久的朋友也知道函數調用是怎麼實現的,即調用一個已經封裝好的函數,實現某個特定的功能。

把一個或者多個功能通過函數的方式封裝起來,對外只提供一個簡單的函數介面,然後在其他地方調用即可


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();
        }
    }
}

上述示例中通過函數指針的方式間接調用了應用層的函數,而且並不違背分層設計原則。
如果看代碼不能立即理解的話,可以嘗試通過下圖理解:

 Callback 是一個函數指針類型的變數,通過函數 FML_TIME_Attach 拿到了應用層代碼函數 OnFunction(...) 的函數地址,之後在定時器中斷函數中根據觸發條件調用 Callback 即可,調用方式和直接調用 OnFunction(...) 沒有太大差異,只不過名字不一樣(可以理解成取了一個別名),為了保證系統運行安全,調用前要確保  Callback 不為 NULL,否則會引起程式異常。

 

本文來自博客園,作者:大橙子瘋,轉載請註明原文鏈接:https://www.cnblogs.com/const-zpc/p/16364419.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 Emmet是一款文本編輯器/IDE的插件,用來快速生成複雜的HTML代碼,只要掌握一些常用的語法(類似於CSS選擇器),就可以減少重覆編碼的工作,真的提升開發效率之利器。 所有的操作都是按下tab鍵即可瞬間完成。 一、相關語法 1. 用.來生成類名 div.aaa 按tab後生成如下: <di ...
  • html標簽屬性大全 html標簽屬性大全從網上搜集整理的常用html標簽,供朋友們交流學習html用。 html標簽<marquee> <marquee>...</marquee>普通捲動 <marquee behavior=slide>...</marquee>滑動 <marquee behav ...
  • 以下是小編整理的部分html關鍵字,專門給有需要的朋友進行打字練慣用,通過打字練習的方式,既提高了打字速度,又可以熟悉html關鍵字~~~ www, url, http, W3C, html, htm, head, body, title, br, hr, font, font-style, fon ...
  • 1、瀏覽器列印相對還是比較簡單的,但也導致控制起來就麻煩。對於簡單的列印需求直接用js調用window.print()即可。如果想要更好的控制列印結果,那這個就不能滿足了。 2、市面上有挺多專門做列印功能的商用插件,要不收費,要不免費版閹割有水印,這在公司里使用自然是不行的。也有第三方的插件可用。但 ...
  • 1、數字格式化 JS版-直接寫到原型鏈上 /** * @author: silencetea * @name: * @description: 數字格式化,預設每三位用英文逗號分隔 * @param {number} number 要格式化的數字 * @param {number} decimals ...
  • 50 Projects 01 Expanding Cards(附帶新手菜雞註釋) Live Demo HTML: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compati ...
  • 涉及到的鏈接: W3school-JavaScript教程 JavaScript簡介 文檔對象模型 (DOM) JavaScript BOM(瀏覽器對象模型) JS面向對象之原型 JavaScript基於原型的面向對象編程 js的變數(詳解) 關於JavaScript作用域的理解 簡單談談JavaS ...
  • 1、前言 單例模式屬於創建型模式,保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。 單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例,這個類稱為單例類,它提供全局訪問的方法。 2、介紹 2.1、主要解決 防止一個系統全局使用的類頻繁地創建與銷毀、解決多線程併發訪問的問題 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...