為什麼函式入棧順序從右往左?
最近在閱讀《程式設計師的自我修養》,看到10.2節,又想起以前的入棧順序,對此又深挖了一下:
大家都知道:
1.函式入棧順序通常是:從右到左
2.從右到左的好處是,第一個引數就在棧頂,我們很方便就定位到了第一個引數的位置
3.網上一搜,大家都說,從右往左入棧的目的是方便的可變引數的使用,獲得第一個引數的位置,
好的,大部分的討論終結於此,那麼,為什麼我們要獲得第一個引數的位置呢?為什麼獲得第一個引數就方便可變引數的使用和實現呢?
接下來,我們回想一下,可變引數的函式是如何使用的,不得不說到下面幾個函式:
void va_start(va_list ap, last); type va_arg(va_list ap, type); void va_end(va_list ap); void va_copy(va_list dest, va_list src);
va_list是用於存放參數列表的資料結構。
va_start函式根據初始化last來初始化引數列表。
va_arg函式用於從引數列表中取出一個引數,引數型別由type指定。
va_copy函式用於複製引數列表。
va_end函式執行清理引數列表的工作。
引用自http://www.jb51.net/article/43192.htm
我們在使用可變引數的時候,一定會使用va_start這個函式,準確的說,它是一個巨集:
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
也就是說,我們在使用可變引數的時候,必須要給出第一個引數,這樣,va_start才能初始化後續的列表,而要定位第一個引數,直接用ebp-4即可(這裡的減4是減去返回地址)
回答最初的問題:
1.為什麼要獲得第一個引數的位置:首先,C語言庫使用可變引數函式時,就必須要求有第一個引數,獲得第一個引數,可以定位後續的引數。這裡又會想出一個問題,那麼,為什麼C語言庫要至少有1個引數,我個人理解是,有一個引數可以方便使用者處理後續的可變引數,因為如果你的程式碼中寫死了後續引數的型別,你不如直接用固定引數,那麼既然你使用了可變引數,就說明後續的型別個數,你可能是不確定的,你可以通過第一個引數來確定(當然,是否這樣確定,如何確定由你的程式碼實現,參見printf的實現)
2.如果沒有第一個引數,會怎樣?我想了下,沒有第一個引數,那麼你整個引數的型別,是沒法確定的(排除有的腦袋進水,能確定型別還是用可變引數的人),當然,要確定也不是沒有辦法,比如,你可以把引數型別寫入一個配置檔案中,讀取,即配置檔案實現了第一個引數的功能。那麼基於此,還不如有一個固定的引數,讓使用者能夠方便的獲取可變引數的型別和個數。
3.基於1,2,可以知道為什麼要有第一個引數,而不允許全部是可變引數?如果理解了1,2,既然C語言設計要求一定要有第一個引數,那麼第一個引數就要方便獲取,如果從右往左壓棧,那麼,第一個引數的地址直接ebp-4即可;如果從左往右壓棧,由於可變引數的大小型別不知道,很難通過ebp去定位第一個引數的位置