首先,回顧一下基礎的宏操做:linux
#
與##
#
的做用是字符串化:在一個宏中的參數前面使用一個#,預處理器會把這個參數轉換爲一個字符數組數組
#define ERROR_LOG(info) fprintf(stderr,"error:"#info"\n");
則有:函數
ERROR_LOG("add"); ---> fprintf(stderr,"error: "add"\n"); ERROR_LOG(devied =0); ---> fprintf(stderr,"error: devied=0\n");
#
是一種分隔鏈接方式,它的做用是先分隔,而後進行強制鏈接。工具
例如:優化
#define XNAME(n) x##n
那麼XNAME(4)
就會展開爲x4
.3d
do{/*codes*/}while(0)
採用這種方式是爲了防範在使用宏過程當中出現錯誤,主要有以下幾點:指針
(1)空的宏定義避免warning
:code
#define foo() do{}while(0)
(2)存在一個獨立的block
,能夠用來進行變量定義,進行比較複雜的實現。
(3)若是出如今判斷語句事後的宏,這樣能夠保證做爲一個總體來是實現:blog
#define foo() \ action1(); \ action2();
在遇到分支語句時:字符串
if(NULL == pPtr) foo();
foo()
中的兩個語句就不會都被執行。
(4)爲什麼不用單獨的{}
#define switch(x,y) {int tmp; tmp=x;x=y;y=tmp;} if(x>y) switch(x,y); else op();
在把宏引入代碼中,會多出一個分號,從而會報錯。
···
與__VA_ARGS__
某些函數接受可變的參數例如printf()
,在頭文件stdvar.h
中有工具能夠自定義變參宏。
把宏參數列表中最後的參數用···
省略,而__VA_ARGS__
可用在替換部分,表面省略號表明的東西。
#define PR(···) printf(__VA_ARGS__)
例如:
PR("THIS IS __VA_ARGS__");
會被展開爲:
printf("THIS IS __VA_ARGS__");
符號 | 樣例值 | 含義 |
---|---|---|
__FILE__ |
"test.c" |
進行編譯的文件名 |
__LINE__ |
25 |
當前行的行號 |
__DATE__ |
"Jan 31 2001" |
被編譯的日期 |
__TIME__ |
"23:17:24" |
被編譯的時間 |
__STDC__ |
1 |
是否遵循ANSI C |
__FUNCTION__ |
main |
所在函數名稱 |
這些宏與編譯器有關,有些支持有些不支持.
以下程序:
運行結果爲:
注意到: 若是用函數或內聯函數,每次的行號便都會相同。
下面是內核中,常見的兩個宏:
該宏的定義以下:
#define __offsetof__(type, member) ( ( size_t ) & ( ( type * ) 0 )->member )
做用是獲取結構體某成員變量的偏移量。
分析以下:
(type *)0
將0轉化爲該類型的指針,即地址爲0x00000000((type *)0)->member
訪問成員member
&(((type *)0)->member)
獲取該成員地址(也就是其偏移量)(size_t)&(((type *)0)->member)
將地址轉化爲size_t
類型 即偏移量這裏訪問0指針爲什麼不會報錯,取決於gcc
對於該過程的優化,不會直接訪問空間而是直接得到地址.
該宏的定義以下:
#define __container_of__(ptr, type, member) ({\ const typeof ( ( ( type * ) 0 ) -> member ) *__mptr=(ptr);\ ( type * )( ( char * )__mptr - __offsetof__( type, member ) );\ })
要理解這段宏,須要知道幾個GCC C EXTENSIONS
,查閱GCC MANUAL
:
1.Statements and Declarations in Expressions
2.Referring to a Type with typeof
手冊中也給出了一個典型的用法示例:
這段宏的分析以下:
typeof()
爲GNU C
,得到變量類型typeof (((type *)0 )->member)
起始地址爲0
再獲取member
最後返回member類型const typeof (((type *)0 )->member) * __mptr=(ptr)
定義 __
mptr 指針,指向ptr指向的地址,併成爲常量指針(char *)__mptr
__mptr
轉化爲字符型指針(運算以1個字節爲單位)- __offsetof__(type,member))
減去該成員的偏移量(type*)( ( char * )__mptr - __offsetof__(type,member))
最後轉化爲指向該類型的指針(指向該類型的首地址)關於上述兩個宏的一段程序以下:
#include <stdio.h> #include <string.h> /** * 獲取結構體變量成員的偏移量 * @param type 類型(struct) * @param member 成員 */ #define __offsetof__(type, member) ( ( size_t ) & ( ( type * ) 0 )->member ) /** * 獲取指向整個結構體的指針 * @param ptr 指向成員(member)變量的指針 * @param type 類型(struct) * @param member 成員變量 */ #define __container_of__(ptr, type, member) ({\ const typeof ( ( ( type * ) 0 ) -> member ) *__mptr=(ptr);\ ( type * )( ( char * )__mptr - __offsetof__( type, member ) );\ }) typedef struct Student { char gender; int id; int age; char name[20]; double score; } Stu; int main() { int gender_offset,id_offset,age_offset,name_offset,score_offset; gender_offset = __offsetof__(struct Student, gender); id_offset = __offsetof__(struct Student, id); age_offset = __offsetof__(struct Student, age); name_offset = __offsetof__(struct Student, name); score_offset = __offsetof__(struct Student, score); printf("%d\t%d\t%d\t%d\t%d\n", gender_offset, id_offset, age_offset, name_offset, score_offset); Stu stu; Stu *pstu; stu.gender = '1'; stu.id = 9527; stu.age = 18; stu.score = 98.2; strcpy(stu.name, "elioyang"); pstu = __container_of__(&stu.id, Stu, id); printf("gender=%c\n", pstu->gender); printf("age=%d\n", pstu->age); printf("name=%s\n", pstu->name); printf("score=%lf", pstu->score); return 0; }
運行結果以下:
0 4 8 12 32 gender=1 age=18 name=elioyang score=98.200000