在c/c++中,對函式引數的掃瞄是從後向前的。c/c++的函式引數是通過壓入堆疊的方式來給函式傳引數的(堆疊是一種先進後出的資料結構),最先壓入的引數最後出來printf的第乙個被找到的引數就是那個字元指標,就是被雙引號括起來的那一部分,函式通過判斷字串裡控制引數的個數來判斷引數個數及資料型別,通過這些就可算出資料需要的堆疊指標的偏移量了。
引數是最後的先壓入棧中,最先的後壓入棧中,引數控制的那個字串常量是最後被壓入的,所以這個常量總是能被找到的。
c語言的函式是從下(低位址)向上(高位址)壓入堆疊的,如下圖所示:
棧底 高位址
| …| 函式返回位址
| …| 函式最後乙個引數
| …| 函式第乙個可變引數 <–va_start後ap指向
| 函式最後乙個固定引數
| …| 函式第乙個固定引數
棧頂 低位址
實現變參的關鍵資料結構:
typedef int * va_list;
//va_list等價於int *即整型指標,該變數型別應該根據具體的架構(arm,x86)確定
#define va_start(ap, a) (ap = (int *)&(a) + 1)
//(int *)&得到a所在的位址,並強制型別轉換為int ,然後這個位址加上a的大小,則使ap指向第乙個可變引數!!
#define va_arg(ap, t) ((t *)ap++)
//先對指標ap(即位址)進行強制型別轉換,轉換為該變數實際的型別, 然後ap(即當前位址)自加1(即加乙個型別的大小,指向下乙個可變引數的位址),這類應該注意的是,先使用後自加,然後取出該位址的值!!
#define va_end(ap) ((void)0)
精簡版prinf的例子:
#define va_start(ap, a) (ap = (int *)&(a) + 1)
#define va_arg(ap, t) (*(t *)ap++)
#define va_end(ap) ((void)0)
int printf(const char * format, ...)
break;
default:
putchar(c); //輸出字串中為普通字元的字元
break;
} }return 0;
}
實際上就是在第乙個固定引數裡面解析出來各種字元輸出控制變數,然後根據變數型別去棧中偏移相應的位址找到引數實際值,然後根據不同格式呼叫不同的輸出函式即可完成變參顯示printf函式。 C語言中變參函式傳參的實現示例
目錄 近期在看一本書,叫做 嵌入式c語言自我修養 寫的內容對我幫助很大,是一本好書。在第6章 c編譯器擴充套件語法精講一節,這本書給出了一些變參函式的例子 1.變參函式初體驗 include void print num int count,int main void 上面的 很好理解 定義乙個變參...
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...
C語言變參函式的原理與應用
變參函式其實並不罕見,在初學者的的 helloworld 程式中就用到了變參函式 printf 在之後的學習當中,scanf 又是另乙個常見的變參函式。這些函式有以下共同特點 在函式呼叫過程中,相應記憶體的棧空間會增長,這時函式引數會被壓到棧中。在普通函式執行時,函式根據引數列表得知棧內的引數型別以...