歡迎使用CSDN markdown編輯器

2021-07-05 02:58:43 字數 2452 閱讀 2700

源貼鏈結

首先,要實現類似printf()的變參函式,函式的最後乙個引數要用 … 表示,如

int log(char * arg1, ...)這樣編譯器才能知道這個函式是變參函式。這個引數與變參函式的內部實現完全沒有關係,只是讓編譯器在編譯呼叫此類函式的語句時不計較引數多少老老實實地把全部引數壓棧而不報錯,當然…之前至少要有乙個普通的引數,這是由實現手段限制的。

c語言通過幾個巨集實現變參的定址。下面是linux2.18核心原始碼裡這幾個巨集的定義,相信符合c89,c99標準的c語言基本都是這樣定義的。

typedef char *va_list;

/*

storage alignment properties -- 堆疊按機器字對齊

*/#define _aupbnd (sizeof (acpi_native_uint) - 1)

#define _adnbnd (sizeof (acpi_native_uint) - 1)

/* variable argument list macro definitions -- 變參函式內部實現需要用到的巨集

*/#define _bnd(x, bnd) (((sizeof (x)) + (bnd)) & (~(bnd)))

#define va_arg(ap, t) (*(t *)(((ap) += (_bnd (t, _aupbnd))) - (_bnd (t,_adnbnd))))

#define va_end(ap) (void) 0

#define va_start(ap, a) (void) ((ap) = (((char *) &(a)) + (_bnd (a,_aupbnd))))

下面以x86 32位機為例分析這幾個巨集的用途

要理解這幾個巨集需要對c語言如何傳遞引數有一定了解。與pascal相反,與stdcall 相同,c語言傳遞引數時是用push指令從右到左將引數逐個壓棧,因此c語言裡通過棧指標來訪問引數。雖然x86的push一次可以壓2,4或8個位元組入 棧,c語言在壓引數入棧時仍然是機器字的size為最小單位的,也就是說引數的位址都是字對齊的,這就是_bnd(x,bnd)存在的原因。另外補充一點 常識,不管是彙編還是c,編譯出的x86函式一般在進入函式體後立即執行

push ebp

mov ebp, esp

這兩條指令。首先把ebp入棧,然後將當前棧指標賦給ebp,以後訪問棧裡的引數都使用ebp作為基指標。

一一解釋這幾個巨集的作用。

_bnd(x,bnd),計算型別為x的引數在棧中佔據的位元組數,當然是字對齊後的位元組數了。acpi_native_unit是乙個機器字,32位機的定義是:typedef u32 acpi_native_uint;

顯然,_aupbnd_adnbnd的值是 4-1 == 3 == 0x00000003 ,按位取反( ~(bnd))就是0xfffffffc 。

因此,_bnd(x,bnd)巨集在32位機下就是

( (sizeof(x) + 3)&0xfffffffc )

很明顯,其作用是–倘若sizeof(x)不是4的整數倍,去餘加4。

_bnd(sizeof(char),3) == 4 

_bnd(sizeof(struct size7struct),3) == 8

va_start(ap,a),初始化引數指標ap,將函式引數a右邊第乙個引數的位址賦給ap。 a必須是乙個引數的指標,所以此種型別函式至少要有乙個普通的引數啊。像下面的例子函式,就是將第二個引數的指標賦給ap。

va_arg(ap,t),獲得ap指向引數的值,並使ap指向下乙個引數,t用來指明當前引數型別。注意((ap) += (_bnd (t, _aupbnd)))是被一對括號括起來的,然後才減去(_bnd (t, _adnbnd),而_aupbnd和_adnbnd是相等的。所以取得的值是ap當前指向的引數值,但是先給ap加了當前引數在字對齊後所佔的位元組數,使其指向了下乙個引數。

va_end(ap), 作用是美觀。

3 總結

先用乙個 … 引數宣告函式是變參函式,接下來在函式內部以va_start(ap,a)巨集初始化引數指標,然後就可以用va_arg(ap,型別)從左到右逐個獲取引數值了

分析到此處算是一清二白了,下面給乙個例子

int

log(char * fmt,...)

va_end(ap);

}

歡迎使用CSDN markdow

本markdown編輯器使用stackedit修改而來,用它寫部落格,將會帶來全新的體驗哦 markdown 是一種輕量級標記語言,它允許人們使用易讀易寫的純文字格式編寫文件,然後轉換成格式豐富的html頁面。維基百科 使用簡單的符號標識不同的標題,將某些文字標記為粗體或者斜體,建立乙個鏈結等,詳細...

歡迎毛毛與妞妞使用CSDN markdown編輯器

建立乙個自定義列表 如何建立乙個註腳 注釋也是必不可少的 katex數學公式 新的甘特圖功能,豐富你的文章 uml 圖表 flowchart流程圖 匯出與匯入 你好!這是你第一次使用markdown編輯器所展示的歡迎頁。如果你想學習如何使用markdown編輯器,可以仔細閱讀這篇文章,了解一下mar...

歡迎使用CSDN markdow1n編輯器

本markdown編輯器使用stackedit修改而來,用它寫部落格,將會帶來全新的體驗哦 markdown 是一種輕量級標記語言,它允許人們使用易讀易寫的純文字格式編寫文件,然後轉換成格式豐富的html頁面。維基百科 使用簡單的符號標識不同的標題,將某些文字標記為粗體或者斜體,建立乙個鏈結等,詳細...