宏定義及相關用法

宏定義及相關用法

歡迎各位補充

目錄

  • 一些成熟軟件中常用的宏定義:
  • 使用一些內置宏跟蹤調試:
  • 宏定義防止使用時錯誤:
  • 宏與函數
    • 帶副作用的宏參數
  • 特殊符號:’#’、’##’
    • 1、一般用法
    • 2、當宏參數是另一個宏的時候
  • __VA_ARGS__與##__VA_ARGS__

一些成熟軟件中常用的宏定義:

1,防止一個頭文件被重復包含

#ifndef COMDEF_H
#define COMDEF_H//頭文件內容 …
#endif

2,重新定義一些類型,防止由于各種平臺和編譯器的不同,而產生的類型字節數差異,方便移植。

typedef  unsigned long int  uint32;      	/* Unsigned 32 bit value */

3,得到指定地址上的一個字節或字

#define  MEM_B( x )  ( *( (byte *) (x) ) )
#define  MEM_W( x )  ( *( (word *) (x) ) )

4,求最大值和最小值

#define  MAX( x, y )  ( ((x) > (y)) ? (x) : (y) )
#define  MIN( x, y )  ( ((x) < (y)) ? (x) : (y) )

5,得到一個field在結構體(struct)中的偏移量

#define FPOS( type, field )   ( (dword) &(( type *) 0)-> field )

6, 得到一個結構體中field所占用的字節數

#define FSIZ( type, field ) sizeof( ((type *) 0)->field )

7,按照LSB格式把兩個字節轉化為一個word

#define  FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )

8,按照LSB格式把一個word轉化為兩個字節

#define  FLOPW( ray, val ) \
(ray)[0] = ((val) / 256); \
(ray)[1] = ((val) & 0xFF)

9,得到一個變量的地址(word寬度)

#define  B_PTR( var )  ( (byte *) (void *) &(var) )
#define  W_PTR( var )  ( (word *) (void *) &(var) )

10,得到一個字的高位和低位字節

#define  WORD_LO(xxx)  ((byte) ((word)(var) & 255))
#define  WORD_HI(xxx)  ((byte) ((word)(var) >> 8))

11,返回一個比X大的最接近的8的倍數

#define RND8( x )       ((((x) + 7) / 8 ) * 8 )

12,將一個字母轉換為大寫

#define  UPCASE( ch ) ( ((ch) >= ’a' && (ch) <= ’z') ? ((ch) - 0×20) : (ch) )

13,判斷字符是不是10進值的數字

#define  DECCHK( ch ) ((ch) >= ’0′ && (ch) <= ’9′)

14,判斷字符是不是16進值的數字

#define  HEXCHK( ch ) \
(((ch) >= ’0′ && (ch) <= ’9′) || \
((ch) >= ’A' && (ch) <= ’F') || \
((ch) >= ’a' && (ch) <= ’f') )

15,防止溢出的一個方法

#define  INC_SAT( val )  (val = ((val)+1 > (val)) ? (val)+1 : (val))

16,返回數組元素的個數

#define  ARR_SIZE( a )  ( sizeof( (a) ) / sizeof( (a[0]) ) )

17,對于IO空間映射在存儲空間的結構,輸入輸出處理

#define inp(port)         (*((volatile byte *) (port)))
#define inpw(port)        (*((volatile word *) (port)))
#define inpdw(port)       (*((volatile dword *)(port)))#define outp(port, val)   (*((volatile byte *) (port)) = ((byte) (val)))
#define outpw(port, val)  (*((volatile word *) (port)) = ((word) (val)))
#define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))

18,字符串拼接打印日志
兩個相鄰字符串會被拼接成一個字符串的技巧。每行都需要使用雙引號將字符串引起來再加反斜杠,否則字符串將會把每一行前面的空格包含進去。

#define DEBUG_PRINT printf("File %s line %d :"\"x=%d,y=%d,z=%d",\__FILE__,__LINE,x,y,z)

使用一些內置宏跟蹤調試:

ANSI標準定義了幾個個預定義的宏名。它們包括但不止于:

__LINE__
__FILE__
__DATE__
__TIME__
__STDC__

注: 常用的還有__FUNCTION__等【非標準】,詳細信息可查看: Predefined Macros,如果編譯不是標準的,則可能僅支持以上宏名中的幾個,或根本不支持。記住編譯程序 也許還提供其它預定義的宏名。可以定義宏,例如:當定義了_DEBUG,輸出數據信息和所在文件所在行

#ifdef _DEBUG
#define DEBUGMSG(msg,date) printf(msg);printf("%d%d%d", date, __LINE__, __FILE__)
#else
#define DEBUGMSG(msg,date)
#endif

宏定義防止使用時錯誤:

用小括號包含。

//例如:
#define ADD(a,b) ((a)+(b))

用do{}while(0)語句包含多語句防止錯誤(注意while(0)后無分號).

//例如:
#difne DO(a,b) a+b; a++;
//應寫成:
#difne DO(a,b) do{a+b; a++;}while(0)

為什么需要do{…}while(0)形式?大致有以下幾個原因:

  • 空的宏定義避免warning:
    #define foo() do{}while(0)

  • 存在一個獨立的block,可以用來進行重復性變量定義,進行比較復雜的實現。

  • 如果出現在判斷語句過后的宏,這樣可以保證作為一個整體來是實現:

#define foo(x)
action1();
action2();
//在以下情況下:
if(NULL == pPointer)foo();
//就會出現action2必然被執行的情況,而這顯然不是程序設計的目的。
  • 以上的第3種情況用單獨的{}也可以實現,但是為什么一定要一個do{}while(0)呢,看以下代碼:
#define switch(x,y) {int tmp; tmp=x;x=y;y=tmp;}
if(x>y)switch(x,y);
else        //error, parse error before elseotheraction();

在把宏引入代碼中,會多出一個分號,從而會報錯。使用do{….}while(0) 把它包裹起來,成為一個獨立的語法單元,從而不會與上下文發生混淆。同時因為絕大多數的編譯器都能夠識別do{…}while(0)這種無用的循環并進行優化,所以使用這種方法也不會導致程序的性能降低,【但是并非所有情況都用這種形式,有些情況不需要,有些情況則不能夠夠】。


宏與函數

宏相比函數而言的優勢主要在于:

  1. 宏因為是文本替換,沒有函數棧的維護代價
  2. 宏參數不帶類型,可以做函數不能做的工作。

解釋一下,第一點 函數調用是利用函數棧幀來實現的,這個需要一定的系統資源。但是宏是直接文本替換,無函數調用過程。
第二點是因為函數的參數列表要求每個參數必須帶類型,宏不需要(亦可代替函數重載),當我們想實現把類型也當作參數傳入時,函數是無法做到。例如下面的宏:

#define MALLOC(size,type) ((type*)malloc(sizeof(type) * (size)))
int * p = MALLOC(10, int);

這個宏有點厲害了,傳入大小和類型兩個參數,然后動態分配出所需要的內存。這個肯定是無法用函數實現的。這個是不是與 C++ 的模版有點相像?模版能夠實現的技術基礎就是能夠把參數類型當作一種參數,而宏正好就支持!!!

然而,宏的文本替換大法這么厲害,為什么還會有函數的存在嘞?
最主要的原因是:宏的文本替換大法在處理帶副作用的宏參數時會導致不可預料的后果。

帶副作用的宏參數

什么叫帶副作用的宏參數?副作用是指某些運算符在求值的同時也會永久地改變操作數,比如:i+1

這個表達式求值沒有副作用,這個式子不管運算多少次,式子的值不變。但是:
++i

卻不一樣了,它運算 100 次與運算 1 次的結果是不一樣的,因為每次運算都會永久改變 i 的值。

那當副作用遇上宏參數會發生什么呢?看一個經典例子:

#define MAX(a,b) ((a)>(b)?(a):(b))
int a = 0;
int b = 1;
int c = MAX(a++, b++);

根據宏替換大法,我們看c被預編譯器處理后的樣子:

int c = ((a++)>(b++)?(a++):(b++));

ok,副作用一目了然。因為 a++

int max(a, b)
{return a>b?a:b;
}
int a = 0;
int b = 1;
int c = max(a++, b++);

這么執行的結果就是
c = 1,a = 1,b = 2;

這是我們想要的。
上面兩者的區別在于,使用函數時,只在參數列表里面進行一次帶副作用的運算;使用宏時,在宏的列表,宏的內容都執行了帶副作用的運算。而我們通常只希望副作用運算執行一次。


特殊符號:’#’、’##’

1、一般用法

我們使用#把宏參數變為一個字符串,用##把兩個宏參數貼合在一起.例如:

#include<cstdio>
#include<climits>
using namespace std;#define STR(s)     #s
#define CONS(a,b)  int(a##e##b)int main()
{// 輸出字符串vckprintf(STR(vck));						// 2e3 輸出:2000,科學計數法printf("%d\n", CONS(2,3));					return 0;
}

2、當宏參數是另一個宏的時候

需要注意的是凡宏定義里有用#或##的地方宏參數是不會再展開.

沒有’#'和’##’的情況

#define TOW      (2)
#define MUL(a,b) (a*b)
printf("%d * %d = %d\n", TOW, TOW, MUL(TOW,TOW));

這行的宏會被展開為:
printf("%d * %d = %d\n", (2), (2), ((2) * (2)));
MUL 里的參數 TOW 會被展開為(2).

當有’#'或’##’的時候

#define A          (2)
#define STR(s)     #s
#define CONS(a,b)  int(a##e##b)
printf("int max: %s\n",  STR(INT_MAX));    // INT_MAX #include<climits>
printf("%s\n", CONS(A, A));               // compile error

第一個 printf() 這行會被展開為:
printf(“int max: %s\n”, #INT_MAX);

第二個 printf() 則是:
printf("%s\n", int(AeA)); //編譯錯誤

INT_MAX 和 A 都不會再被展開, 然而解決這個問題的方法很簡單. 加多一層中間轉換宏;加這層宏的用意是把所有宏的參數在這層里全部展開, 那么在轉換宏里的那一個宏(_STR)就能得到正確的宏參數.

#define A           (2)
#define _STR(s)     #s
#define STR(s)      _STR(s)                 // 轉換宏
#define _CONS(a,b)  int(a##e##b)
#define CONS(a,b)   _CONS(a,b)       		// 轉換宏printf(“int max: %s\n”, STR(INT_MAX));
輸出為: int max: 0x7fffffff
STR(INT_MAX) –> _STR(0x7fffffff) 然后再轉換成字符串;printf("%d\n", CONS(A, A));
輸出為:200
CONS(A, A) –> _CONS((2), (2)) –> int((2)e(2))

’#'和’##’的一些應用特例:

合并匿名變量名,例:
#define  __ANONYMOUS1(type, var, line)  type  var##line
#define  _ANONYMOUS0(type, line)  __ANONYMOUS1(type, _anonymous, line)
#define  ANONYMOUS(type)  _ANONYMOUS0(type, __LINE__)ANONYMOUS(static int);
//即:
static int _anonymous70;  	//70表示該行行號;

① 第一層:ANONYMOUS(static int); –> __ANONYMOUS0(static int, __LINE__);
② 第二層:–> ___ANONYMOUS1(static int, _anonymous, 70);
③ 第三層:–> static int _anonymous70;
即每次只能解開當前層的宏,所以__LINE__在第二層才能被解開;

填充結構
#define  FILL(a)   {a, #a}enum IDD{OPEN, CLOSE};
typedef struct MSG{IDD id;const char * msg;
}MSG;MSG _msg[] = {FILL(OPEN),FILL(CLOSE)
};
//相當于:
MSG _msg[] = {{OPEN, “OPEN”},{CLOSE, ”CLOSE“}
};
記錄文件名
#define  _GET_FILE_NAME(f)   #f
#define  GET_FILE_NAME(f)    _GET_FILE_NAME(f)
static char  FILE_NAME[] = GET_FILE_NAME(__FILE__);
得到一個數值類型所對應的字符串緩沖大小
#define  _TYPE_BUF_SIZE(type)  sizeof #type
#define  TYPE_BUF_SIZE(type)   _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT_MAX)];
char buf[_TYPE_BUF_SIZE(“0x7fffffff”)];
char buf[sizeof “0x7fffffff”];
這里相當于:
char buf[11];

__VA_ARGS__與##__VA_ARGS__

\_\_VA_ARGS__ 是一個可變參數的宏,很少人知道這個宏,這個可變參數的宏是新的 C99 規范中新增的,目前似乎只有 gcc 支持(VC6.0的編譯器不支持)。實現思想就是宏定義中參數列表的最后一個參數為省略號(也就是三個點)。
##\_\_VA_ARGS__ 宏,在 \_\_VA_ARGS__ 前面加上 ## 的作用在于,當可變參數的個數為0時,這里的 ## 起到把前面多余的逗號去掉的作用,否則會編譯出錯。

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

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

相關文章

解決:Cannot read property ‘component‘ of undefined ( 即 vue-router 0.x 轉化為 2.x)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 vue項目原本是用0.x版本的vue-router&#xff0c;但是去報出&#xff1a;Cannot read property component of undefined 這是因為版本問…

AMD Mantle再添新作,引發下代GPU架構猜想

摘要&#xff1a;今年秋天即將發布的《希德梅爾文明&#xff1a;太空》將全面支持AMD Mantle API&#xff0c;如此強大的功能背后離不開強大的CPU、GPU支持。上周AMD爆出了下一代海盜島R9 300系列&#xff0c;據網友猜測海盜島家族可能用上速度更快的HBM堆棧式內存。 小伙伴們…

不作35歲的程序員?

程序員三部曲--不作35歲的程序員?摩西2000 在中國&#xff0c;程序員不能超過35歲&#xff0c;似乎已經是不爭的事實&#xff0c;軟件開發工作就是青春飯&#xff0c;頂多靠畢業這十年的時間&#xff0c;超過這個年齡&#xff0c;要不成功躍身成為管理者&#xff0c;要不轉…

linux下使用TC模擬弱網絡環境

linux下使用TC模擬弱網絡環境 模擬延遲傳輸簡介 netem 與 tc: netem 是 Linux 2.6 及以上內核版本提供的一個網絡模擬功能模塊。該功能模塊可以用來在性能良好的局域網中,模擬出復雜的互聯網傳輸性能,諸如低帶寬、傳輸延遲、丟包等等情 況。使用 Linux 2.6 (或以上) 版本內核…

CDN 是什么 、CDN 引入

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 CDN 的全稱是 Content Delivery Network&#xff0c;即內容分發網絡。 CDN的基本原理是廣泛采用各種緩存服務器&#xff0c;將這些緩存…

長壽的人會有的8個健康理念

長壽的人會有的8個健康理念。年輕的時候總是在揮霍身體健康&#xff0c;吸煙、喝酒沒有節制&#xff0c;到老了之后身體會出現各種問題。老年人如果想要身體健康、長壽的話&#xff0c;就要從日常生活習慣做起。下面小編就來介紹長壽的人會有的8個健康理念&#xff1a; 1、少…

Ubuntu下selenium+Chrome的安裝使用

Ubuntu下seleniumChrome的安裝使用 安裝 chrome 官網下載安裝包 sudo dpkg -i google-chrome-stable_current_amd64.deb whereis google-chrome 安裝selenium pip3 install selenium 下載chromedriver(火狐使用geckodriver)驅動 http://npm.taobao.org/mirrors/chromed…

shoot for用法

Look, there are people like Ross who need to shoot for the stars, with his museum, and his papers getting published.---《老友記》 而像羅斯這種人則追求卓越&#xff0c;博物館&#xff0c;發表論文。 爭取;為...而努力Were shooting this year for a 50% increase in…

VUE : 雙重 for 循環寫法、table 解析任意 list 、萬能表格組件、解析一維數組、動態生成 table 所有數據

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1.需求&#xff1a; 我想要一個 table 組件能在實際調用時動態生成所有的 tr 、td 。 后端返回的只是一個 list &#xff0c; 前端頁…

安全離職妙招

高招的離職&#xff0c;不但有可能讓前老板幫你說好話&#xff0c;讓前同事成為你的啦啦隊&#xff0c;未來若有好機會&#xff0c;還會想到你&#xff0c;只要你學會克服離職流程中的五個尷尬情境。 情境一、離職怎么提&#xff1f; 口頭請辭&#xff0c;最先告知上司。 有…

字節內推~

大佬們有興趣來字節約飯么&#xff0c;下面是內推鏈接~ 社招內推鏈接&#xff1a;https://job.toutiao.com/s/LwpKWU8 校招內推鏈接&#xff1a;https://job.toutiao.com/s/LwsFw6g

使用編輯工具快速創建實體對象的方法

快速創建java類 (\w)\s(.) /** $2 */\nprivate String $1; search Mode 為 Reqular expression 轉載于:https://www.cnblogs.com/otways/p/11283303.html

超詳細 圖解 : IntelliJ IDEA 逆向生成 JAVA 實體類

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1.配置數據庫,&#xff0c;這里連接的是mysql。 2.填寫 連接數據庫的信息&#xff0c;填寫完成后可以點擊Test Connection,測試一下是否…

用面粉和醋洗頭 讓你的頭發黑亮又濃密

用面粉和醋洗頭發&#xff0c;別看這些最廉價、最普通的東西&#xff0c;卻能帶來意想不到的效果。調配這種洗頭液很簡單&#xff0c;取50&#xff5e;100克面粉&#xff0c;加入少許涼水調成稀面糊&#xff0c;倒入沸水中煮開&#xff0c;然后加入25&#xff5e;50克醋&#x…

leetcode練習——數組篇(1)(std::ios::sync_with_stdio(false);std::cin.tie(nullptr);)

題號1. 兩數之和&#xff1a; 給定一個整數數組 nums 和一個目標值 target&#xff0c;請你在該數組中找出和為目標值的那 兩個 整數&#xff0c;并返回他們的數組下標。 你可以假設每種輸入只會對應一個答案。但是&#xff0c;你不能重復利用這個數組中同樣的元素。 示例: …

intellij idea 中去除 @Autowired 注入對象帶來的紅色下劃線報錯提示

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 PS&#xff1a; 有 2 種方法&#xff0c;第 2 種方法更簡單&#xff0c;在此謝謝好心友人的評論。 方法1&#xff1a; idea中通過Autow…

根據目標選擇減肥方法 少做無用功

不同的美體目標適合的減肥方法也是不同的&#xff0c;有些人想減去大部分體重&#xff0c;而有些人只是想讓身體曲線更柔美&#xff0c;這就要求有相應的減肥方法&#xff0c;對癥下藥&#xff0c;才會讓減肥少做無用功。 目標&#xff1a;我想穿上小一碼的衣服 建議&#x…

Coolite動態加載CheckboxGroup,無法在后臺中獲取

Coolite在后臺動態加載CheckboxGroup&#xff0c;頁面顯示都正常&#xff0c;但是在后臺去獲取選中的checkbox時&#xff0c;使用下方法&#xff1a; ///<summary>///獲取所選權限 ///</summary>///<returns></returns>privatestringGetPermiss…

基于java的數據結構學習——動態數組C++類模板(含拷貝構造,重載常見運算符)

之前實現了java的動態數組&#xff0c;試著寫了個C版的&#xff0c;同樣對時間復雜度振蕩進行了處理。純手打&#xff0c;代碼如下 &#xff1a; // // Created by PC-Saw on 2018/12/19. //#ifndef DATA_STRUCTURE_MYARRAY_H #define DATA_STRUCTURE_MYARRAY_H#include <i…

科目三考試過程詳解

科目三是考駕照的最后一項考試&#xff0c;所以考生在這關都很注意&#xff0c;但是有可能是由于過于緊張都難免會有些失誤&#xff0c;如果沒過的話&#xff0c;那也就意味著您拿照的時間又延長了另外還要交補考費。因此很多學員都想一次性把這項考試通過&#xff0c;那么我們…