版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、從printf談可變參數(shù)函數(shù)的實現(xiàn)作者:戎亞新摘要:一直以來都覺得printf似乎是c語言庫中功能最強大的函數(shù)之一,不僅因為它能格式化輸出,更在于它的參數(shù)個數(shù)沒有限制,要幾個就給幾個,來者不拒。printf這種對參數(shù)個數(shù)和參數(shù)類型的強大適應(yīng)性,讓人產(chǎn)生了對它進(jìn)行探索的濃厚興趣。 關(guān)鍵字:printf, 可變參數(shù) 1. 使用情形 int a =10;double b = 20.0;char *str = "Hello world"printf("begin printn");printf("a=%d, b=%.3f, str=%sn",
2、 a, b, str);.從printf的使用情況來看,我們不難發(fā)現(xiàn)一個規(guī)律,就是無論其可變的參數(shù)有多少個,printf的第一個參數(shù)總是一個字符串。而正是這第一個參數(shù),使得它可以確認(rèn)后面還有有多少個參數(shù)尾隨。而尾隨的每個參數(shù)占用的??臻g大小又是通過第一個格式字符串確定的。然而printf到底是怎樣取第一個參數(shù)后面的參數(shù)值的呢,請看如下代碼 2. printf 函數(shù)的實現(xiàn) /acenv.htypedef char *va_list;#define _AUPBND (sizeof (acpi_native_int) - 1)#define _ADNBND (sizeof (acpi_native_
3、int) - 1) #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)/start.cstatic char sprint_buf1024;int printf(char *fmt, .)va
4、_list args;int n;va_start(args, fmt);n = vsprintf(sprint_buf, fmt, args);va_end(args);write(stdout, sprint_buf, n);return n;/unistd.hstatic inline long write(int fd, const char *buf, off_t count)return sys_write(fd, buf, count);3. 分析 從上面的代碼來看,printf似乎并不復(fù)雜,它通過一個宏va_start把所有的可變參數(shù)放到了由args指向的一塊內(nèi)存中,然后再調(diào)用
5、vsprintf. 真正的參數(shù)個數(shù)以及格式的確定是在vsprintf搞定的了。由于vsprintf的代碼比較復(fù)雜,也不是我們這里要討論的重點,所以下面就不再列出了。我們這里要討論的重點是va_start(ap, A)宏的實現(xiàn),它對定位從參數(shù)A后面的參數(shù)有重大的制導(dǎo)意義。現(xiàn)在把 #define va_start(ap, A) (void) (ap) = (char *) &(A) + (_bnd (A,_AUPBND) 的含義解釋一下如下: va_start(ap, A) char *ap = (char *)(&A) + sizeof(A)并int類型大小地址對齊 在print
6、f的va_start(args, fmt)中,fmt的類型為char *, 因此對于一個32為系統(tǒng) sizeof(char *) = 4, 如果int大小也是32,則va_start(args, fmt);相當(dāng)于 char *args = (char *)(&fmt) + 4; 此時args的值正好為fmt后第一個參數(shù)的地址。對于如下的可變參數(shù)函數(shù) void fun(double d,.) va_list args; int n; va_start(args, d); 則 va_start(args, d);相當(dāng)于 char *args = (char *)&d + sizeo
7、f(double);此時args正好指向d后面的第一個參數(shù)。 可變參數(shù)函數(shù)的實現(xiàn)與函數(shù)調(diào)用的棧結(jié)構(gòu)有關(guān),正常情況下c/c+的函數(shù)參數(shù)入棧規(guī)則為_stdcall, 它是從右到左的,即函數(shù)中的最右邊的參數(shù)最先入棧。對于函數(shù) void fun(int a, int b, int c) int d; . 其棧結(jié)構(gòu)為 0x1ffc->d 0x2000->a 0x2004->b 0x2008->c對于任何編譯器,每個棧單元的大小都是sizeof(int), 而函數(shù)的每個參數(shù)都至少要占一個棧單元大小,如函數(shù) void fun1(char a, int b, double c, sho
8、rt d) 對一個32的系統(tǒng)其棧的結(jié)構(gòu)就是 0x1ffc->a (4字節(jié)) 0x2000->b (4字節(jié)) 0x2004->c (8字節(jié)) 0x200c->d (4字節(jié))對于函數(shù)void fun1(char a, int b, double c, short d) 如果知道了參數(shù)a的地址,則要取后續(xù)參數(shù)的值則可以通過a的地址計算a后面參數(shù)的地址,然后取對應(yīng)的值,而后面參數(shù)的個數(shù)可以直接由變量a指定,當(dāng)然也可以像printf一樣根據(jù)第一個參數(shù)中的%模式個數(shù)來決定后續(xù)參數(shù)的個數(shù)和類型。如果參數(shù)的個數(shù)由第一個參數(shù)a直接決定,則后續(xù)參數(shù)的類型如果沒有變化并且是已知的,則我們可以
9、這樣來取后續(xù)參數(shù), 假定后續(xù)參數(shù)的類型都是double; void fun1(int num, .) double *p = (double *)(&num)+1); double Param1 = *p; double Param2 = *(p+1); . double Paramn *(p+num);如果后續(xù)參數(shù)的類型是變化而且是未知的,則必須通過一個參數(shù)中設(shè)定模式來匹配后續(xù)參數(shù)的個數(shù)和類型,就像printf一樣,當(dāng)然我們可以定義自己的模式,如可以用i表示int參數(shù),d表示double參數(shù),為了簡單,我們用一個字符表示一個參數(shù),并由該字符的名稱決定參數(shù)的類型而字符的出現(xiàn)的順序也表示
10、后續(xù)參數(shù)的順序。 我們可以這樣定義字符和參數(shù)類型的映射表, i-ints-signed shortl-longc-char "ild"模式用于表示后續(xù)有三個參數(shù),按順序分別為int, long, double類型的三個參數(shù)那么這樣我們可以定義自己版本的printf 如下 void printf(char *fmt, .) char s80 = "" int paramCount = strlen(fmt); write(stdout, "paramCount = " , strlen(paramCount = ); itoa(para
11、mCount,s,10); write(stdout, s, strlen(s); char *p = (char *)(&fmt) + sizeof(char *); int *pi = (int *)p; for (int i=0; i<paramCount; i+) char line80 = "" strcpy(line, "param"); itoa(i+1, s, 10); strcat(line, s); strcat(line, "="); switch(fmti) case 'i': c
12、ase 's': itoa(*pi),s,10); strcat(line, s); pi+; break; case 'c': int len = strlen(line); linelen = (char)(*pi); linelen+1 = '0' break; case 'l': ltoa(*(long *)pi),s,10); strcat(line, s); pi+; break; default: break; 也可以這樣定義我們的Max函數(shù),它返回多個輸入整型參數(shù)的最大值 int Max(int n, .) int
13、 *p = &n + 1; int ret = *p; for (int i=0; i<n; i+) if (ret < *(p + i) ret = *(p + i); return ret;可以這樣調(diào)用, 后續(xù)參數(shù)的個數(shù)由第一個參數(shù)指定 int m = Max(3, 45, 12, 56);int m = Max(1, 3);int m = Max(2, 23, 45);int first = 34, second = 45, third=5;int m = Max(5, first, second, third, 100, 4);結(jié)論 對于可變參數(shù)函數(shù)的調(diào)用有一點需要注意,實際的可變參數(shù)的個數(shù)必須比前面模式指定的個數(shù)要多,或者不小于, 也即后續(xù)參數(shù)多一點不要緊,但不能少, 如果少了則會訪問到函數(shù)參數(shù)以外的堆棧區(qū)域,這可能會把程序搞崩掉。前面模式的類型和后面實際參數(shù)的類型不匹配也有可能造成把程序搞崩潰,只要模式指定的數(shù)據(jù)長度大于后續(xù)參數(shù)長度,則這種情況就會發(fā)生。如:printf("%.3f, %.3f, %.
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 【正版授權(quán)】 ISO 2475:2025 EN Chloroprene rubber (CR) - General-purpose types - Evaluation procedure
- 2025裝飾裝修工程分包合同
- 2025技術(shù)傳授合同樣式
- 二零二五年度高端裝備價格保密合同3篇
- 2025年度綠色建筑示范項目建房協(xié)議書3篇
- 二零二五年度企業(yè)內(nèi)部停車場車輛使用免責(zé)協(xié)議3篇
- 二零二五年度智能家居系統(tǒng)發(fā)起人投資合同3篇
- 二零二五年度歷史文化街區(qū)物業(yè)用房移交及文化保護(hù)協(xié)議3篇
- 二零二五年度社區(qū)食堂兼職煮飯人員協(xié)議3篇
- 二零二五年度內(nèi)部員工保密協(xié)議模板:企業(yè)核心競爭力保護(hù)3篇
- 2025年上半年河南省西峽縣部分事業(yè)單位招考易考易錯模擬試題(共500題)試卷后附參考答案-1
- 深交所創(chuàng)業(yè)板注冊制發(fā)行上市審核動態(tài)(2020-2022)
- 手術(shù)室護(hù)理組長競聘
- 電力系統(tǒng)繼電保護(hù)試題以及答案(二)
- 小學(xué)生防打架斗毆安全教育
- 2024-2025學(xué)年九年級英語上學(xué)期期末真題復(fù)習(xí) 專題09 單詞拼寫(安徽專用)
- 網(wǎng)絡(luò)運營代銷合同范例
- 2024年新人教版七年級上冊歷史 第14課 絲綢之路的開通與經(jīng)營西域
- 植保無人機安全飛行
- 醫(yī)療糾紛事件匯報
- 2024年村干部個人工作總結(jié)例文(3篇)
評論
0/150
提交評論