首先,回顧一下基礎的宏操作:
C語言宏
#與##
-
#的作用是字串化:在一個宏中的引數前面使用一個#,前處理器會把這個引數轉換為一個字符陣列#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.
do{/*codes*/}while(0)
采用這種方式是為了防范在使用宏程序中出現錯誤,主要有如下幾點:
(1)空的宏定義避免warning:
#define foo() do{}while(0)
(2)存在一個獨立的block,可以用來進行變數定義,進行比較復雜的實作,
(3)如果出現在判斷陳述句過后的宏,這樣可以保證作為一個整體來是實作:
#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 |
所在函式名稱 |
這些宏與編譯器有關,有些支持有些不支持.
如下程式:

運行結果為:

注意到: 如果用函式或行內函式,每次的行號便都會相同,
下面是內核中,常見的兩個宏:
Linux常用的兩個宏
offsetof
該宏的定義如下:
#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對于該程序的優化,不會直接訪問空間而是直接獲得地址.
container_of
該宏的定義如下:
#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
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/3625.html
標籤:C
