17 C 語言宏進階必看:從宏替換避坑到宏函數用法,不定參數模擬實現一次搞定

?預處理詳解

1. 預定義符號

//C語?設置了?些預定義符號,可以直接使?,預定義符號也是在預處理期間處理的。
__FILE__ //進?編譯的源?件--預處理階段被替換成指向文件名字符串的指針--char* 類型的變量
__LINE__ //?件當前的?號 --預處理階段替換成使用該預處理符號所在的行號--unsigned int 類型
__DATE__ //?件被編譯的?期
__TIME__ //?件被編譯的時間
__STDC__ //如果編譯器遵循ANSI C,其值為1,否則未定義

2 #define 定義常量

宏替換注意點:

1 只能替換在同一行的內容,如果內容太長可以在每一行結尾用 \表示緒行

2 宏替換本質上就是文本替換在預處理階段就替換完成了,c語言代碼叫編譯型語言是因為在沒有編譯之前

代碼就是文本文件,是可以進行替換的。替換之后再做語法檢查

基本語法:

#define name stuff 1//這個只是宏替換的模板,后面使用到name字段按照這個模板替換成stuff 1

舉個例?:

#define MAX 1000
#define reg register //為 register這個關鍵字,創建?個簡短的名字
#define do_forever for(;;) //?更形象的符號來替換?種實現
#define CASE break;case //在寫case語句的時候?動把 break寫上。
// 如果定義的 stuff過?,可以分成??寫,除了最后??外,每?的后?都加?個反斜杠(續?符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
思考:在define定義標識符的時候,要不要在最后加上 ; ?
//?如:
#define MAX 1000;
#define MAX 1000//建議不要加上 ; ,這樣容易導致問題。
//?如下?的場景:
if(condition)
max = MAX;
else
max = 0;//如果是加了分號的情況,等替換后,if和else之間就是2條語句,?沒有?括號的時候,if后邊只能有?
//條語句。這?會出現語法錯誤。

3 #define定義宏

#define機制包括了?個規定,允許把參數替換到?本中,這種實現通常稱為宏(macro)或定義宏 下?是宏的申明?式:

#define name( parament-list ) stuff?

其中的 parament-list 是?個由逗號隔開的符號表,它們可能出現在stuff中。 注意: 參數列表的左括號必須與name緊鄰,如果兩者之間有任何空?存在,參數列表就會被解釋為stuff的 ?部分。

舉例:

#define SQUARE( x ) x * x

這個宏接收?個參數 x .如果在上述聲明之后,你把 SQUARE( 5 ); 置于程序中,預處理器就會?下?這個表達式替換上?的表達式:

5 * 5

警告: 這個宏存在?個問題: 觀察下?的代碼段:

int a = 5;
printf("%d\n" ,SQUARE( a + 1) );

乍?看,你可能覺得這段代碼將打印36,事實上它將打印11,為什么呢?? 替換?本時,參數x被替換成a+1,所以這條語句實際上變成了:

printf ("%d\n",a + 1 * a + 1 );?

這樣就?較清晰了,由替換產?的表達式并沒有按照預想的次序進?求值。 在宏定義上加上兩個括號,這個問題便輕松的解決了:

#define SQUARE(x) (x) * (x)?

這樣預處理之后就產?了預期的效果:

printf ("%d\n",(a + 1) * (a + 1) );

4 宏替換的規則

在程序中擴展#define定義符號和宏時,需要涉及?個步驟。

  1. 在調?宏時,?先對參數進?檢查,看看是否包含任何由#define定義的符號。如果是,它們?先 被替換。

  2. 替換?本隨后被插?到程序中原來?本的位置。對于宏,參數名被他們的值所替換。

  3. 最后,再次對結果?件進?掃描,看看它是否包含任何由#define定義的符號。如果是,就重復上 述處理過程。 注意:

  4. 宏參數和#define定義中可以出現其他#define定義的符號。但是對于宏,不能出現遞歸。

  5. 當預處理器搜索#define定義的符號的時候,字符串常量的內容并不被搜索。

5 宏函數和函數 的對?

注意點宏函數

宏函數的參數的替換是沒有類型檢查的,只是把參數替換進去最后編譯才進行類型檢查。是把宏參數做文本替換,替換掉后面的整個文本中的宏參數文本,比如:

#define MAX(a, b) ((a)>(b)?(a):(b)) int x=1,y=2;
//文本a和b替換成x,y,后面的文本中的a,b也被替換成x,y
MAX(x,y) ---替換成((x)>(y)?(x):(y)) 
//a,b-->1,2
MAX(1,2) ---替換成((1)>(2)?(1):(2)) 
//a,b -->1,2
MAX(1,x) --- ((1)>(x)?(1):(x)) 

宏通常被應?于執?簡單的運算。 ?如在兩個數中找出較?的?個時,寫成下?的宏,更有優勢?些。

#define MAX(a, b) ((a)>(b)?(a):(b))?

那為什么不?函數來完成這個任務? 原因有?:

  1. ?于調?函數和從函數返回的代碼可能?實際執?這個?型計算?作所需要的時間更多。所以宏? 函數在程序的規模和速度??更勝?籌。

  2. 更為重要的是函數的參數必須聲明為特定的類型。所以函數只能在類型合適的表達式上使?。反之 這個宏可以適?于整形、?整型、浮點型等可以?于 > 來?較的類型。宏的參數是類型?關的。 和函數相?宏的劣勢:

  3. 每次使?宏的時候,?份宏定義的代碼將插?到程序中。除?宏?較短,否則可能?幅度增加程序 的?度。

  4. 宏是沒法調試的。

  5. 宏由于類型?關,也就不夠嚴謹。

  6. 宏可能會帶來運算符優先級的問題,導致程容易出現錯。 宏有時候可以做函數做不到的事情。?如:宏的參數可以出現類型,但是函數做不到。


#define MALLOC(num, type)\
(type )malloc(num sizeof(type))
...
//使?
MALLOC(10, int); //類型作為參數
//預處理器替換之后:
(int * )malloc(10 sizeof(int));

6 #和##

1 #運算符

#運算符將宏的?個參數轉換為字符串字?量。它僅允許出現在帶參數的宏的替換列表中。 #運算符所執?的操作可以理解為”字符串化“。 當我們有?個變量 int a = 10; 的時候,我們想打印出: the value of a is 10 . 就可以寫:

//首先把a文本替換掉宏函數中的n文本,然后在把后面的文本中的n文本也替換成a文本
//最后整體替換文本,宏函數替換成后面一整個文本
#define PRINT(n) printf("the value of "#n " is %d", n); 
int a=1;
PRINT(a);// the value of a is 10 .

當我們按照下?的?式調?的時候: PRINT(a);//當我們把a替換到宏的體內時,就出現了#a,?#a就是轉換為 字符串"a",同時兩個字符串是會被合并為一個字符串的,比如:

"abs""sad" ->"abssad" ? ?"add"fff"dsa" --> "addfffdsa"

2 ##運算符

可以把位于它兩邊的符號合成?個符號,它允許宏定義從分離的?本?段創建標識符。 ## 被稱為記號粘合 這樣的連接必須產??個合法的標識符。否則其結果就是未定義的。這?我們想想,寫?個函數求2個數的較?值的時候,不同的數據類型就得寫不同的函數。?如:

int int_max(int x, int y)
{
return x>y?x:y;
}
float float_max(float x, float y)
{
return x>yx:y;
}

但是這樣寫起來太繁瑣了,現在我們這樣寫代碼試試: //宏定義

#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
return (x>y?x:y); \
}

使?宏,定義不同函數 GENERIC_MAX(int) //替換到宏體內后int##max ?成了新的符號 int_max做函數名 GENERIC_MAX(float) //替換到宏體內后float##max ?成了新的符號 float_max做函數名


GENERIC_MAX(int) 
/*
預處理被文本替換成
int int_max(int x,int y)
{return ((x)>(y)?(x):(y));
}*/
GENERIC_MAX(float)
int main()
{
//調?函數
int m = int_max(2, 3);
printf("%d\n", m);
float fm = float_max(3.5f, 4.5f);
printf("%f\n", fm);
return 0;
}
輸出:
3
4.500000
不定參數的傳參使用##
#include<stdio.h>//__FILE__ :預處理符號,在預處理階段替換成當前符號所在的文件名類型為字符串
//__LINE__ :預處理符號, 在預處理階段替換成當前符號所在的文件的具體行數類型為int//不定宏參數的使用: ... 可變參數符號,不確定傳入的參數是多少,在宏定義中通常搭配##__VA_ARGS__使用
//Print(fmt,...) 預處理替換時 Print(fmt,...)中的fmt 替換printf中的fmt ,"jjjj"替換 ...
//##__VA_ARGS__:若可變參數為空,C99標準要求逗號必須保留(可能引發語法錯誤)。
//GCC/Clang通過##__VA_ARGS__優化,展開時自動去掉前面的逗號
//傳入Print(不定參數) 通過##__VA_ARGS__替換到printf函數中
//當多個字符串寫在一起時編譯預處理時會自動把多個字符串拼接在一起
//"ddd""dasd""asdsa" --> "ddddsdadsadsa"
#define Print(fmt,...) printf("[%s %d]"fmt"\n",__FILE__,__LINE__,##__VA_ARGS__)
int main()
{printf("[%s %d] %s\n",__FILE__,__LINE__,"jhhh");Print("%s","jjjj");Print("gggg");//傳入的可變參數為空Print("ddd""fff");return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/95837.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/95837.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/95837.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深入剖析 HarmonyOS ArkUI 聲明式開發:狀態管理藝術與最佳實踐

好的&#xff0c;請看這篇關于 HarmonyOS ArkUI 聲明式開發范式與狀態管理的技術文章。 深入剖析 HarmonyOS ArkUI 聲明式開發&#xff1a;狀態管理藝術與最佳實踐 引言 隨著 HarmonyOS 4、5 的廣泛應用以及面向未來的 HarmonyOS NEXT&#xff08;API 12&#xff09;的發布&…

Qwen-Code安裝教程

一、概述Qwen Code 是一個強大的基于命令行、面向開發者的 AI 工作流工具&#xff0c;改編自 Gemini CLI&#xff0c;專門針對 Qwen3-Coder 模型進行了優化。它專門為代碼理解、代碼重構、自動化工作流、Git 操作等場景設計&#xff0c;讓你的開發工作變得更高效、更智能。它既…

老師傅一分鐘精準判斷電池好壞!就靠這個神器!

在汽車維修與保養領域&#xff0c;蓄電池狀態的準確判斷一直是技術人員面臨的重要挑戰。傳統的電壓測量方法只能反映表面現象&#xff0c;無法深入評估蓄電池的實際健康狀態。Midtronics MDX-P300蓄電池及電氣系統測試儀作為專業級診斷設備&#xff0c;通過電導測試技術和多系統…

Axure筆記

Axure介紹 快速原型的軟件 應用場景&#xff1a;拉投資、給項目團隊、銷售演示、項目投標、內部收集反饋、教學 軟件安裝與漢化 漢化&#xff1a;復制lang文件夾和三個dll 軟件的基礎功能 基本布局&#xff1a;菜單欄、工具欄、頁面和摘要、元件和母版、畫布、樣式交互和說明設…

Pytorch Yolov11 OBB 旋轉框檢測+window部署+推理封裝 留貼記錄

Pytorch Yolov11 OBB 旋轉框檢測window部署推理封裝 留貼記錄 上一章寫了下【Pytorch Yolov11目標檢測window部署推理封裝 留貼記錄】&#xff0c;這一章開一下YOLOV11 OBB旋轉框檢測相關的全流程&#xff0c;有些和上一章重復的地方我會簡寫&#xff0c;要兩篇結合著看&#x…

《Keil 開發避坑指南:STM32 頭文件加載異常與 RTE 配置問題全解決》

《Keil 開發避坑指南&#xff1a;STM32 頭文件加載異常與 RTE 配置問題全解決》文章提綱一、引言? 簡述 Keil 在 STM32 開發中的核心地位&#xff0c;指出頭文件加載和 RTE&#xff08;運行時環境&#xff09;配置是新手常遇且關鍵的問題&#xff0c;說明本文旨在為開發者提…

TortoiseGit 2.4.0.0 64位安裝教程(附詳細步驟和Git配置 附安裝包)

本教程詳細講解 ?TortoiseGit 2.4.0.0 64位版本? 的完整安裝步驟&#xff0c;包括如何運行 ?TortoiseGit-2.4.0.0-64bit.msi? 安裝包、設置安裝路徑、關聯 Git 環境&#xff0c;以及安裝后的基本配置方法&#xff0c;適合 Windows 用戶快速上手 Git 圖形化管理工具。 一、…

大數據畢業設計選題推薦-基于大數據的高級大豆農業數據分析與可視化系統-Hadoop-Spark-數據可視化-BigData

?作者主頁&#xff1a;IT畢設夢工廠? 個人簡介&#xff1a;曾從事計算機專業培訓教學&#xff0c;擅長Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等項目實戰。接項目定制開發、代碼講解、答辯教學、文檔編寫、降重等。 ?文末獲取源碼? 精彩專欄推薦?…

學習機器學習能看哪些書籍

關注B站可以觀看更多實戰教學視頻&#xff1a;hallo128的個人空間 在機器學習與深度學習的知識海洋中&#xff0c;選擇合適的書籍往往是入門和進階的關鍵。以下四本經典著作各具特色&#xff0c;覆蓋了從基礎理論到實踐應用的多個維度&#xff0c;無論你是初學者還是有一定基礎…

Unity通過Object學習原型模式

原型模式簡述 依據現有的實例生成新的實例 Object的實例化方法 Object.Instantiate 克隆 original 對象并返回克隆對象 Unity中的實例&#xff1a;預制體或場景中的游戲對象 示例 方法1&#xff1a;手動創建對象并添加組件 方法2&#xff1a;使用實例化方法&#xff0c;實…

【踩坑記錄】Unity 項目中 PlasticSCM 掩蔽列表引發的 文件缺失問題排查與解決

問題描述&#xff1a; Plastic SCM 簽入時&#xff0c;彈窗提示“項xxx在該工作區中不存在” Unity 項目中 PlasticSCM 掩蔽列表引發的 文件缺失問題排查與解決 文章目錄Unity 項目中 PlasticSCM 掩蔽列表引發的 文件缺失問題排查與解決一、前言二、Unity 與 .meta 文件機制1. …

Redis實戰-附近的人實現的解決方案

1.GEO數據結構1.1實現附近的人的數據結構Redis提供的專用的數據結構來實現附近的人的操作&#xff0c;這也是企業的主流解決方案&#xff0c;建議使用這種解決方案。GEO就是Redis提供的地理坐標計算的一個數據結構&#xff0c;可以很方便的計算出來兩個地點的地理坐標&#xff…

HTML第七課:發展史

HTML第七課&#xff1a;發展史發展史快速學習平臺發展史 示例 HTML 發展史 前端三件套&#xff1a;html 、css、javascript(Js) HTML 發展史 HTML 1.0&#xff08;1993 年&#xff09; 蒂姆伯納斯 - 李&#xff08;Tim Berners - Lee&#xff09;發明了萬維網&#xff0c;同…

中國生成式引擎優化(GEO)市場分析:領先企業格局與未來趨勢分析

一、GEO市場變革中國生成式引擎優化&#xff08;Generative Engine Optimization, GEO&#xff09;市場正經歷一場深刻的變革&#xff0c;其核心在于生成式人工智能&#xff08;Generative AI&#xff09;對傳統搜索引擎和數字營銷模式的顛覆性影響。傳統搜索引擎以“提供鏈接”…

好看的背景顏色 uniapp+小程序

<view class"bg-decoration"><view class"circle-1"></view><view class"circle-2"></view><view class"circle-3"></view> </view>/* 背景裝飾 */.container{background: linear-gr…

《駕馭云原生復雜性:隱性Bug的全鏈路防御體系構建》

容器、服務網格、動態配置等抽象層為系統賦予了彈性與效率,但也像深海中的暗礁,將技術風險隱藏在標準化的接口之下。那些困擾開發者的隱性Bug,往往并非源于底層技術的缺陷,而是對抽象層運行邏輯的理解偏差、配置與業務特性的錯配,或是多組件交互時的協同失效。它們以“偶發…

vosk語音識別實戰

一、簡介 Vosk 是一個由 Alpha Cephei 團隊開發的開源離線語音識別&#xff08;ASR&#xff09;工具包。它的核心優勢在于完全離線運行和輕量級&#xff0c;使其非常適合在資源受限的環境、注重隱私的場景或需要低延遲的應用中使用。 二、核心特點 離線運行 (Offline) 這是…

鴻蒙ABC開發中的名稱混淆與反射處理策略:安全與效率的平衡

在當今的軟件開發中&#xff0c;代碼安全是一個至關重要的議題。隨著鴻蒙系統&#xff08;HarmonyOS&#xff09;的廣泛應用&#xff0c;開發者們在追求功能實現的同時&#xff0c;也必須考慮如何保護代碼不被輕易破解。名稱混淆是一種常見的代碼保護手段&#xff0c;但當反射機…

css頁面頂部底部固定,中間自適應幾種方法

以下是實現頁面頂部和底部固定、中間內容自適應的幾種常見方法&#xff0c;附代碼示例和適用場景分析&#xff1a;方法一&#xff1a;Flexbox 彈性布局 <body style"margin:0; min-height:100vh; display:flex; flex-direction:column;"><header style"…

徹底拆解 CSS?accent-color:一個屬性,省下一堆“重造輪子”的苦工

我有一支技術全面、經驗豐富的小型團隊&#xff0c;專注高效交付中等規模外包項目&#xff0c;有需要外包項目的可以聯系我既要原生控件、又要品牌配色&#xff0c;還不想偽造組件&#xff1f;能不能講透 accent-color。下面給出一版盡量“到骨頭里”的解析&#xff1b;對討厭從…