網站首頁 編程語言 正文
C語言中帶返回值的宏定義
相信大家在實際工作中,一定有遇到需要編寫一個宏定義,且希望它能帶返回值的場景吧?
比如我之前就遇到一個場景,早期的代碼是使用函數實現的功能,現在想換成宏定義,但是又要保留之前調用函數的代碼不動,這樣我就只能想辦法寫一個帶返回值的宏了。
宏定義編寫
直接上demo:
#include <stdio.h> /* always return 1 */ #define RETURN_MACRO()?? ??? ??? ?({do {} while(0);1;}) #define RETURN_MACRO2()?? ??? ??? ?1 /* return a+b */ #define A_PLUS_B_MACRO(a, b)?? ?({int ret; ret = (a) + (b); ret;}) #define A_PLUS_B_MACRO2(a, b)?? ?({int ret; ret = add((a), (b)); ret;}) int add(int a, int b) { ?? ?return (a + b); } int main(int argc, const char *argv[]) { ?? ?int a = 6; ?? ?int b = 7; ?? ?printf("Hello world !\n"); ?? ?printf("RETURN_MACRO: %d\n", RETURN_MACRO()); ?? ?printf("RETURN_MACRO2: %d\n", RETURN_MACRO2()); ?? ?printf("a + b = %d\n", A_PLUS_B_MACRO(a, b)); ?? ?printf("a + b = %d\n", A_PLUS_B_MACRO2(a, b)); ?? ?return 0; }
宏定義分析
為了分析宏定義的寫法,我們得知道宏定義最終被展開是什么樣的。
我在之前的博文中有提到,使用gcc編譯器的話,可以在CFLAGS上加上-save-temps=obj這個編譯選項,這樣就可以得到預編譯處理之后的文件,后綴名是.i。
我們使用編譯腳本編譯之后,得到.i文件如下:
//前面的內容忽略 # 3 "main.c" 2 # 12 "main.c" # 12 "main.c" int add(int a, int b) { ?return (a + b); } int main(int argc, const char *argv[]) { ?int a = 6; ?int b = 7; ?printf("Hello world !\n"); ?printf("RETURN_MACRO: %d\n", ({do {} while(0);1;})); ?printf("RETURN_MACRO2: %d\n", 1); ?printf("a + b = %d\n", ({int ret; ret = (a) + (b); ret;})); ?printf("a + b = %d\n", ({int ret; ret = add((a), (b)); ret;})); ?return 0; }
從.i文件我們可以看到,宏定義被正常展開,下面確認下功能是否正常。
宏定義驗證
我們執行編譯出來的可執行文件:
return_macro$ ./test? Hello world ! RETURN_MACRO: 1 RETURN_MACRO2: 1 a + b = 13 a + b = 13
驗證成功。
經驗總結
- 在C語言里面,可以使用({aaa; bbb; ccc;})來實現宏定義帶返回值;這里的返回值是最后一個;的值。
- 注意里面的()和{}都不能少,否則可能會破壞代碼的語法結構,導致得不到正確的答案。
C語言中一些宏定義和常用的函數
typeof 關鍵字
如果你是 C++ 程序員,應該接觸過 C++11 里的 decltype 操作符,它的作用是自動推導表達式的數據類型,以解決泛型編程中有些類型由模板參數決定而難以(甚至不可能)表示的問題。
其實這個特性在 C 語言中也早有類似的實現,GNU C 標準中的一個擴展特性 typeof 作用與 decltype 類似。
__typeof__ (ret) errnum = (ret);?
snprintf()函數的作用
#include<stdio.h> int snprintf(char* dest_str,size_t size,const char* format,...);
【函數功能】:
先將可變參數 “…” 按照format的格式格式化為字符串,然后再將其拷貝至dest_str中。
如果格式化后的字符串長度小于size,則將字符串全部拷貝至dest_str中,并在字符串結尾處加上‘\0’; 如果格式化后的字符串長度大于或等于size,則將字符串的(size-1)拷貝至dest_str中,然后在字符串結尾處加上’\0’. 函數返回值是 格式化字符串的長度。
__builtin_expect的作用
__builtin_expect(errnum != 0, 0)
這個指令是gcc引入的,作用是"允許程序員將最有可能執行的分支告訴編譯器"。
這個指令的寫法為:__builtin_expect(EXP, N)。意思是:EXP==N的概率很大。
一般的使用方法是將__builtin_expect指令封裝為LIKELY和UNLIKELY宏。
C語言中常用的預定義
-
__LINE__
:當前程序行的行號,表示為十進制整型常量 -
__FILE__
:當前源文件名,表示字符串型常量 -
__DATE__
:轉換的日歷日期,表示為Mmm dd yyyy 形式的字符串常量,Mmm是由asctime產生的。 -
__TIME__
:轉換的時間,表示"hh:mm:ss"形式的字符串型常量,是有asctime產生的。(asctime貌似是指的一個函數) -
__STDC__
:編輯器為ISO兼容實現時位十進制整型常量 -
__func__
:它指示所在的函數 -
__assert_perror_fail
:打印一條包含錯誤碼ERRNUM的錯誤消息,并終止程序
反斜杠的作用
反斜杠起到換行作用,用于宏定義和字符串換行。其中宏定義中使用居多。
如果一行代碼有很多元素,導致太長影響閱讀,可以通過在結尾加\的方式,實現換行,編譯時會忽略\及其后的換行符,當做一行處理。
#define CHECK_ACTION_RETURN(expr) \ ? ? if (!expr) { \ ? ? ? ? printf(":failed(%d)\n", ret); \ ? ? ? ? return ret; \ ? ? ? ? ? ? ? ? } else { \ ? ? ? ? printf(":ok\n"); \ ? ? ? ? ? ? ? ? }
總結
原文鏈接:https://blog.csdn.net/szullc/article/details/121297749
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-12-14 Docker中容器數據卷詳解_docker
- 2023-02-10 批處理從html格式(接收到的郵件)中讀取數據的操作方法_DOS/BAT
- 2022-04-23 Android自定義View實現數字雨效果的全過程_Android
- 2022-09-30 python計算列表元素與乘積詳情_python
- 2022-07-26 Python文件系統模塊pathlib庫_python
- 2022-03-29 python中apply函數詳情_python
- 2022-12-06 R語言如何畫豎線、橫線、添加標簽以及畫固定長度的線段_R語言
- 2022-12-19 Android硬件解碼組件MediaCodec使用教程_Android
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支