天天看點

Delphi中實作變長函數筆記

  前面從網絡中收藏加轉貼了一部分關于變長參數函數的筆記。反正休閑沒事,于是就專門的研究研究了如何用Delphi來實作類似于C語言中Printf這種可以傳遞任意多個參數的函數。這個實際上來說也不太難,隻要俺們都熟悉函數的調用規則,那麼就很容易了。實際上這個變長,總體上來說,應該是有迹可循的。那這個迹象在哪裡,就是關鍵點了,也就是說,最主要的是要知道,這個函數到底傳遞了多少個參數。我們參看C的,printf和Windows的wsPrintf,都知道,實際上他裡面都有一個Format的參數類型。有這個類型,就可以根據這個格式參數,來擷取裡面有多少個%d,%s,%f這樣的比對内容了,于是通過這個,我們就可以知道函數到底傳遞了多少個參數了。繼而,俺們就可以從程式堆棧當中把這些參數一個個的給找出來,然後在俺們的函數中加以處理。那麼這時候肯定就有人要問了,為什麼參數是在堆棧中。。。。這個。。還是歸于函數調用法則來說,最初制定的時候,有規定stdcall,pascal,以及Delphi預設的Register調用都是由程式自動管理堆棧,除此之外,還有一種調用方式就是cdecl這個方式,是由我們自己來平衡堆棧,自己管理的,而動态個數參數的函數,也就必須是這個cdecl的調用方式,這種調用方式,參數是按照從右向左的方式壓入堆棧的,這樣設計是有道理的,從右往左邊壓入,那麼越早傳入的參數就越晚被壓入,那麼我們就能夠知道最有用的實際參數,比如此時如果進入到變長函數,那麼我們就可以知道,目前堆棧的棧頂就是我們的現場,那麼再網上遞歸,就是第一個參數了,也就是說[ebp+8]就是目前的第一個參數了。由此可以找到第二個參數,第三個參數。。。。。。

   是以,我們一定要在最初的參數中擷取後面到底實際傳遞了多少個變體參數。然後才可以通過堆棧,一個個的找到,進而以函數來處理。

說了這麼多來舉例實驗一下。比如,寫一個函數,需要獲得傳入的數字的和

<code>function</code> <code>GetSum(NumCount:</code><code>integer</code><code>{N1,N2: integer}</code><code>):</code><code>Integer</code><code>;cdecl;</code>

<code>asm</code>

<code>  </code><code>{</code>

<code>  </code><code>push ebp</code>

<code>  </code><code>mov ebp,esp</code>

<code>  </code><code>}</code>

<code>  </code><code>mov ecx,[ebp +</code><code>8</code><code>];</code><code>//先擷取第一個參數值,判斷傳遞了幾個參數</code>

<code>  </code><code>//然後擷取每個參數</code>

<code>  </code><code>mov edx,</code><code>12</code>

<code>  </code><code>mov eax,</code><code>0</code>

<code>@@add:</code>

<code>  </code><code>add eax,[ebp].edx</code>

<code>  </code><code>add edx,</code><code>4</code>

<code>  </code><code>loop @@add</code>

<code>end</code><code>;</code>

 然後調用方式:

<code>type</code>

<code>  </code><code>VA_Sum =</code><code>function</code><code>(NumCount:</code><code>integer</code><code>{N1,N2}</code><code>):</code><code>Integer</code><code>; cdecl varargs;</code>

<code>var</code>

<code>  </code><code>m: VA_Sum;</code>

<code>begin</code>

<code>  </code><code>m := GetSum;</code>

<code>  </code><code>ShowMessage(inttostr(m(</code><code>4</code><code>,</code><code>3</code><code>,</code><code>4</code><code>,</code><code>5</code><code>,</code><code>6</code><code>)));</code>

 在比如,連接配接字元串函數

<code>function</code> <code>ConStr(NumCount:</code><code>integer</code><code>{N1,N2}</code><code>):</code><code>string</code><code>;cdecl;</code>

<code>  </code><code>st1:</code><code>string</code><code>;</code>

<code>  </code><code>i:</code><code>Integer</code><code>;</code>

<code>  </code><code>asm</code>

<code>    </code><code>mov esi,</code><code>16</code>

<code>  </code><code>end</code><code>;</code>

<code>  </code><code>for</code> <code>i :=</code><code>0</code> <code>to</code> <code>NumCount -</code><code>1</code> <code>do</code>

<code>  </code><code>begin</code>

<code>    </code><code>asm</code>

<code>      </code><code>mov edi,[ebp].esi</code>

<code>      </code><code>cmp edi,</code><code>128</code>

<code>      </code><code>jb @@</code><code>char</code>

<code>      </code><code>mov DWORD ptr st1,edi</code>

<code>      </code><code>@@</code><code>Char</code><code>:</code>

<code>      </code><code>add esi,</code><code>4</code>

<code>    </code><code>end</code><code>;</code>

<code>    </code><code>Result := Result + st1;</code>

  調用方法

<code>  </code><code>VA_ConStr =</code><code>function</code><code>(NumCount:</code><code>integer</code><code>{N1,N2}</code><code>):</code><code>string</code><code>; cdecl varargs;</code>

<code>  </code><code>cstr: VA_ConStr;</code>

<code>  </code><code>cstr := constr;</code>

<code>  </code><code>ShowMessage(cstr(</code><code>3</code><code>,</code><code>'Test'</code><code>,</code><code>'不得閑'</code><code>,</code><code>'測試'</code><code>));</code>

  

本文轉自 不得閑 部落格園部落格,原文連結: http://www.cnblogs.com/DxSoft/archive/2013/04/18/3028722.html  ,如需轉載請自行聯系原作者

繼續閱讀