相信大家都使用過c語言的庫函式:printf("%d%d", 1, 2)的吧,使用確實很方便功能也很強大。
但是為什麼它可以接受多個引數呢?
現在我們來解析一下多參的實現原理,網上也找了一些文章。發現解析得都不全面。並且有bug。
先看如下原始碼:
#include #include首先,我們函式壓參順序是重右往左,對應的棧空間記憶體位址從高到低。#include
void mysprintf(char* szbuffer, const
char*szformat, ...)
intwinapi winmain (hinstance hinstance, hinstance hprevinstance,
pstr szcmdline,
inticmdshow)
; mysprintf(szbuffer,
"%d%d%d
", 1,2,3
);
return0;
}
mysprintf(szbuffer, "%d%d%d", 1,2,3);
這行**,分別想棧中壓入3, 2, 1, 然後再是szformat的記憶體空間,在是szbuffer的記憶體空間.
記憶體結構如下:
熟悉函式呼叫的幾步過程,壓入引數,儲存返回位址和棧頂,開闢區域性變數空間.
現在儲存了返回位址,然後繼續執行的話,就是儲存棧頂和開闢va_list pa所需的記憶體空間.
va_list其實也就是char* 型別。佔4個位元組。繼續執行後如下:
看到了嗎,va_start(pa, szformat); 這條語句計算出了引數的起始位址,
它是如何計算出的呢?我們既然知道了記憶體布局, 那szformat取位址+sizeof(va_list)。
並且, 以上的mysprintf函式等同於下面這種寫法:
void mysprintf(char* szbuffer, const現在,我們得到了引數的記憶體首位址,但是還缺少資訊。缺什麼資訊?char*szformat, ...)
當前位址處有幾個引數,每個引數什麼型別(占用位元組數)?
關鍵的地方就在這裡了。szformat中有型別資訊資訊,並且有型別資訊的個數,我們可以通過遍歷字串,
找出型別資訊的順序和個數。然後根據遍歷找到的資訊。來解析引數的記憶體首位址。
具體的做法.在vsprintf中有實現,下面是拷貝vsprintf的實現**,
#ifndef _count_本人菜鳥,水平有限,望各路大牛指點!int__cdecl vsprintf (
char *string
,
const
char *format,
va_list ap
)#else /* _count_ */
int__cdecl _vsnprintf (
char *string
, size_t count,
const
char *format,
va_list ap
)#endif /* _count_ */
c的多參設計原理
函式是大多數程式語言都實現的程式設計要素,呼叫函式的實現原理就是 執行跳轉 引數傳遞。對於執行跳轉,所有的cpu都直接提供跳轉指令 對於引數傳遞,cpu會提供多種方式,最常見的方式就是利用棧來傳遞引數。c語言標準實現了函式呼叫,但是卻沒有限定實現細節,不同的c編譯器廠商可以根據底層硬體環境自行確定實...
C語言變參函式的實現原理
1.變參函式簡單示例 include include int accumlate int nr,va end arg return result int main 2.變參函式的實現原理 define va list void define va start arg,start arg va lis...
ajax多參傳遞
ajax 方法通過 http 請求載入遠端資料。該方法是 jquery 底層 ajax 實現。簡單易用的高層實現見 get,post 等。ajax 返回其建立的 xmlhttprequest 物件。大多數情況下你無需直接操作該函式,除非你需要操作不常用的選項,以獲得更多的靈活性。最簡單的情況下,aj...