在https://mp.toutiao.com/profile_v3/graphic/preview?pgc_id=6817822456737169923中,介紹瞭如何建立自己的可變參數函數。
下面繼續介紹可變參數函數的實現原理。
在彙編語言程序設計中,詳細介紹了子程序的實現思想:
(1)子程序只是一個代碼地址;
(2)調用子程序之前,主程序將一些數據存入棧中;
(3)子程序被調用後,從棧中取出數據;
(4)棧底為高地址,棧頂為低地址;
(5)入棧時棧頂指針向低地址方向移動,出棧時,棧頂指針向高地址方向移動。
那C函數各個參數入棧的順序是怎樣的?
我們用一個程序來測試一下參數入棧順序
程序的代碼為:
<code>#include <stdio.h>
void TestOrder(int iFirst, int iSecond, int iThird)
{
printf("First: %ld\\n", (unsigned long)&iFirst);
printf("Second: %ld\\n", (unsigned long)&iSecond);
printf("Third: %ld\\n", (unsigned long)&iThird);
}
void main()
{
TestOrder(1, 2, 3);
}/<stdio.h>/<code>
這是程序的某次運行結果:
<code>First: 140724053717020
Second: 140724053717016
Third: 140724053717012/<code>
入棧時,由於機器的棧頂指針是從高地址向低地址移動。所以我們可以得出結論:
默認情況下,C語言的參數入棧,是從左往右,依次入棧。
因此,main函數調用TestOrder函數時,參數入棧的順序為:
- 值1入棧;
- 值2入棧;
- 值3入棧。
TestOrder函數則直接通過棧的地址,獲得傳入參數,原理是這樣的:
- 棧頂第一個元素的地址,就是iThird形參的地址;
- 棧頂第二個元素的地址,就是iSecond形參的地址;
- 棧頂第三個元素的地址,就是iFirst形參的地址。
如果函數的參數列表是固定的,上面的過程,由編譯器幫我們完成,我們體會不到內部的細節。
如果函數的參數列表是可變的,上面的過程,編譯器已經無能為力,必須由我們自己來完成。
這就是我們使用這套奇怪的標識符的原因了:
- va_list
- va_start
- va_arg
- va_end
本文先講解C函數參數的實現原理,下文將詳細解析這套奇怪的標識符。
謝謝!
閱讀更多 編程實踐 的文章