繼續探索昨天那個問題的深層緣由,爲何要加前綴下劃線呢?今天各方查找,大約是這個意思(也不知道理解的徹底不):編程
函數調用的時候,父子函數間的數據傳遞(參數、返回值),只能依靠兩種硬件:寄存器和內存(不要說磁盤,那速度。。。)!硬件只有兩種,方式能夠有多種:寄存器直接傳送,棧(內存)傳送,混合傳送(寄存器、棧一塊兒來)。每種傳送方式還得明確規定怎麼個傳法:好比多個參數利用棧傳送得規定進出棧的順序,誰(父函數仍是子函數)負責清棧等等。這些函數調用時的數據傳遞規則就叫函數調用約定。編程語言
C 語言函數採用的函數調用約定叫cdecl,cdecl 調用約定規定:函數調用的時候,用棧傳遞參數,用寄存器 eax 傳遞返回值;參數入棧的順序是從右向左,父函數負責清理棧;而且規定,函數名稱之前綴下劃線「_」開頭。——看到沒,這是 C 語言採用的函數調用約定(cdecl )規定的!其餘編程語言要想和 C 語言函數兼容,也必須遵照這個約定,否則 C 語言不認你!函數
不過咱們在編寫 C 語言代碼的時候沒用下劃線前綴啊?調用庫函數的時候也不用在函數名前加下劃線啊?那是由於 C 語言編譯器在編譯的時候都自動幫你加上了!而編寫 NASM 彙編代碼的時候又必須加前綴下劃線,是由於 NASM 編譯器在編譯的時候沒有自動幫你加前綴下劃線,你得本身手工加上,以符合 cdecl 約定,才能被 C 語言函數識別。若是你編寫的彙編語言函數不和 C 語言編寫的函數產生關係,固然就能夠不加前綴下劃線了(廢話!)。lua
再順便擴展下吧,除了 cdecl 約定,固然還有別的約定,好比 Pascal 約定:參數入棧的順序是從左向右、子函數清棧,Pascal 約定恰好和 cdecl 約定相反。再好比還有折中的 stdcll 約定:參數入棧的順序是從右向左(cdecl 約定),子函數負責清理棧(Pascal 約定)。spa
那麼 cdecl 約定有什麼優缺點呢?指針
缺點很明顯:子函數負責清棧的話,無論這個子函數被調用了多少次,清棧代碼只有一份;而父函數負責清棧的話,子函數被調用多少次,就要產生多少份清棧代碼。因此 cdecl 約定的執行代碼體積要膨脹。另外,只使用 eax 寄存器傳送返回值,致使函數只有一個正式的返回值(用指針從參數傳送的另算),而像 lua 語言等用堆棧傳送返回值的能夠有多個返回值。orm
優勢也很明顯:參數從右向左入棧,最後一個入棧(即棧頂)的參數必然是參數表上最左面的那個參數。棧頂這個參數(即參數表上最左面這個參數)是能夠當即訪問的,只要它說明後面的參數個數、類型,那麼子函數就能夠徹底明白其餘參數在棧中的位置,就均可以訪問了。那麼在定義函數的時候,就能夠不固定參數個數了——參數表右面用省略號,表示具體調用前不知道有些什麼參數。只要在調用它的時候,參數表上最左面那個參數交代清楚它右邊的參數個數和類型(就是把省略號交代清楚),就能準確無誤的訪問所有參數了。這就是大名鼎鼎的可變參數函數!內存
正是因爲可變參數函數不知道它本身在被調用的時候要來幾個參數,因此它也無法清棧。只有調用它的父函數知道有幾個參數,因此就必須由父函數清棧。編譯器