精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何使用宏偷梁換柱

454398 ? 來源:alpha007 ? 作者:alpha007 ? 2022-11-15 17:33 ? 次閱讀

有些時候,一些原本的函數功能可能并不是我們想要的,于是就想著修改函數,或者再封裝一層函數。

比如對一個函數包裝:

void func()

{

printf("hello/n");

}

// 包裝函數

void my_func()

{

printf("add/n");

func();

}

打印效果如下:

每打印一個 hello 前面都會增加一個 add。

這樣確實能達到效果,但是因為多調用了一次函數,所以性能會部分下降,同時需要更大的棧空間,那么是否有一種更好的方式去達到相同的目的呢?

有的,那就是使用宏進行偷梁換柱,達到貍貓換太子的目的。

我們以打印函數為例,對它進行偷梁換柱。

一般的打印函數只會打印我們輸入給它的參數,卻無法打印額外的信息,比如時間戳、函數名、文件名、行號等。簡單一點,假如我們希望在打印我們的消息前,能添加時間戳和函數名,又該如何做呢?

簡單且易理解的偷梁換柱如下:

// 定義我們自己的打印函數格式

#define OSPREY_LOG(fmt, ...) rt_kprintf("<%08d>[%s] "fmt"/r/n",/

rt_tick_get(), __FUNCTION__, ##__VA_ARGS__)

#undef printf // 使 printf 在下面失去效用

// 重新定義,此時下面的所有 printf 是一個宏,而不是函數

#define printf(fmt, ...) OSPREY_LOG(fmt, ##__VA_ARGS__)

void osprey_task(void *parameter)

{

uint32_t nbr = 0;

while(1)

{

printf("hello, Osprey %u", nbr++);

rt_thread_delay(1);

}

}

原本我們的代碼里面使用的是 printf 進行打印,但自己比較懶,不想每次換平臺的時候都修改打印函數(比如 RT-Thread 使用 rt_kprintf 打印),或者怕替換的時候操作失誤,那么此時就可以使用這個技巧了。

我們先定義出我們自己的打印格式(關于這個,魚鷹會專門寫一篇筆記介紹如何設計一個簡單實用的日志打印框架,里面會詳細介紹這些內容,目前暫時拿來用就行),這個格式包含了時間戳信息、函數名信息。

之后,使用 #undef 這條預編譯指令取消掉 printf 的作用域,接下來的代碼將不再調用標準庫的函數,而是使用我們自己定義的宏函數 printf,所以我們使用 #define 重新定義 printf。

也就是說,#undef printf 指令后面的代碼將使用 宏函數 printf ,而不是標準庫函數 printf,這是一道分水嶺。

接下來看看打印的效果如何:

可以看到,在我們的 “hello,Osprey”之前,打印了我們需要的時間戳和函數名信息,完美!

通過這些信息,我們就可以知道打印的消息是在什么時候打印的,又是在哪個函數中打印的,定位問題將更加方便(當然你也可以加入文件名和行號,看自己的需要了)。

通過以上三行代碼,我們成功且高效的完成了函數的再次封裝,并且除了這些代碼,不需要對后面的代碼做任何修改,萬一平臺換了,也只需要修改這些代碼就行。

現在再來一個稍微難理解一點的。

既然前面的代碼可以替換 printf 打印函數,那么我們會想,是否可以替換 rt_kprintf 本身呢?

也就是說本來我的代碼就是用 rt_kprintf 函數打印的,我們是否可以對它進行封裝呢?
所以接下來的代碼應運而生:

// 定義我們自己的打印函數格式

#define OSPREY_LOG(fmt, ...) rt_kprintf("<%08d>[%s] "fmt"/r/n",/

rt_tick_get(), __FUNCTION__, ##__VA_ARGS__)

#undef rt_kprintf // 使 rt_kprintf 在下面失去效用

// 重新定義,此時下面的所有 rt_kprintf 是一個宏,而不是函數

#define rt_kprintf(fmt, ...) OSPREY_LOG(fmt, ##__VA_ARGS__)

void osprey_task(void *parameter)

{

uint32_t nbr = 0;

while(1)

{

rt_kprintf("hello, Osprey %u", nbr++);

rt_thread_delay(1);

}

}

當你測試后,你會發現,打印效果和前面的代碼等同,也就是說,通過三條代碼,成功將 rt_kprintf 貍貓換太子了。

其實當你理解了 #undef 和 #define,上面代碼是不難理解的,#undef 取消了 rt_kprintf 的定義,而 #define 又重新定義了 rt_kprintf,所以接下來的:

rt_kprintf("hello, Osprey %u", nbr++);

被替換成了 :

rt_kprintf("<%08d>[%s] "hello, Osprey %u"/r/n",/rt_tick_get(), __FUNCTION__, nbr++)

因為 rt_kprintf 函數已經有了,最后編譯、鏈接的時候也就能順利通過了,爽!

當然,有時候版本發布的時候,我們發現不再需要打印函數了,那么我們只要使用如下方式即可消除打印(文件開頭添加即可,注意 //):

這個騷操作,你學會了嗎?


審核編輯 黃昊宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 函數
    +關注

    關注

    3

    文章

    4306

    瀏覽量

    62431
收藏 人收藏

    評論

    相關推薦

    C語言定義使用技巧

    寫好C語言,漂亮的定義很重要,使用定義可以防止出錯,提高可移植性,可讀性,方便性等等。下面列舉一些成熟軟件中常用的定義。
    發表于 07-29 09:35 ?1101次閱讀

    C語言中的

    定義是我們C語言學習中非常重要的內容。一些基礎的用法大家都比較清楚了,我們簡單總結一下。1.定義的格式為:#define 標識符 字符串。2.定義屬于預處理命令,在編譯過程中的預處理階段處理
    發表于 12-13 15:32

    offsetof與container_of詳解

    offsetof與container_of詳解 1.offsetof與container_of1.1、由結構體指針進而訪問各元素的原理通過結構體整體變量來訪問其中各個元素,本
    發表于 10-13 16:35

    擴展問題

    MPLAB IDIDV3.65和XC8HI,“擴展工具”是一個方便的工具來查看在項目文件中的擴展(右鍵單擊并選擇導航/視圖擴展)。不幸的是,我發現了一個問題:當我在文件中更改
    發表于 04-14 09:57

    什么是

    什么是示例的應用
    發表于 12-15 07:34

    如何設計調試

    前言借調試的設計,梳理下的用法重定向printf打印嵌入式設備基本會配置RS232串口作為調試IO接口,假設底層串口單字節輸出函數為SERIAL_PutChar(),利用fputc()和fputs()重定向printf函數void fputc(int byte, FI
    發表于 12-15 06:13

    C語言中的是什么

    第五章 性能優化5.1 使用定義  在C語言中,是產生內嵌代碼的唯一方法。對于嵌入式系統而言,為了能達到性能要求,是一種很好的代替函數的方法。  寫一個"標準"MIN ,這個
    發表于 12-15 08:20

    excel中的使用方法、技巧和步驟

    excel中的使用方法如下: 一、建立 二、執行 三、編輯和刪除
    發表于 11-19 10:16 ?11.6w次閱讀
    excel中<b class='flag-5'>宏</b>的使用方法、技巧和步驟

    什么是,excel中的作用

    所謂,就是一些命令組織在一起,作為一個單獨命令完成一個特定任務。Microsoft Word中對定義為:“就是能組織到一起作為一獨立的命令使用的一系列word命令,它能使日常工作變得更容易”。Word使用
    發表于 11-19 10:36 ?5.5w次閱讀

    內聯函數和定義的區別介紹

    定義是C語言提供的三種預處理功能的其中一種,這三種預處理包括:定義、文件包含、條件編譯。定義和操作符的區別是:定義是替換,不做計算,也不做表達式求解。
    發表于 12-15 15:33 ?2333次閱讀
    內聯函數和<b class='flag-5'>宏</b>定義的區別介紹

    之剖析

    本講座將探討C預處理器及其預處理器處理。我們將探討一些較高級的示例,并且還會探討與有關的一些誤解,以及預處理器如何展開和對求值。這
    的頭像 發表于 06-07 13:46 ?2339次閱讀
    <b class='flag-5'>宏</b>之剖析

    當使用參數調用時,會將參數替換為主體

    在大多數定義示例中,每次出現的參數名稱都帶有括號,并且另一對括號通常會包圍整個定義,這是編寫最好的方式。舉個例子
    的頭像 發表于 11-16 16:41 ?2269次閱讀

    AD637 SPICE模型

    AD637 SPICE模型
    發表于 06-17 13:43 ?12次下載
    AD637 SPICE<b class='flag-5'>宏</b>模型

    基于select!的進階用法

    Tokio 是一個基于 Rust 語言的異步編程框架,它提供了一組工具和庫,使得異步編程變得更加容易和高效。其中最重要的組件之一就是 select!。 select!是 Tokio 中的一個核心
    的頭像 發表于 09-19 15:35 ?609次閱讀

    如何規范和常量以及命名

    和常量 ◎ 定義和常量使用大寫字母或下劃線。 ◎ 用定義表達式時,要使用完備的括號,如下: #define HEHE_AREA( a , b ) (( a ) * ( b )) ◎
    的頭像 發表于 12-07 14:49 ?681次閱讀