C語言可變參數函數的實現原理

在https://mp.toutiao.com/profile_v3/graphic/preview?pgc_id=6817822456737169923中,介紹瞭如何建立自己的可變參數函數。


C語言可變參數函數的實現原理


下面繼續介紹可變參數函數的實現原理。


在彙編語言程序設計中,詳細介紹了子程序的實現思想:

(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函數參數的實現原理,下文將詳細解析這套奇怪的標識符。


謝謝!



分享到:


相關文章: