程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 剖析C++ Sum函數獲取參數

剖析C++ Sum函數獲取參數

編輯:C++入門知識

在C++中的函數當中,C++ Sum函數可以使用SUM來進行任何求和,但無法使用任何名稱訪問其他的幾個不定參數,但此時由於棧上其他的幾個參數實際恰好依序排列在參數SUM的高地址方向。

因此可以很簡單地通過num的地址計算出其他參數的地址。sum函數的實現如下:

  1. int sum(unsigned num, ...)  
  2.  
  3. {  
  4.  
  5. int* p = &num + 1;  
  6.  
  7. int ret = 0;  
  8.  
  9. while (num--)  
  10.  
  11. ret += *p++;  
  12.  
  13. return ret;  
  14.  

在這裡我們可以觀察到兩個事實:

(1)C++ Sum函數獲取參數的量僅取決於num參數的值,因此,如果num參數的值不等於實際傳遞的不定參數的數量,那麼C++ Sum函數可能取到錯誤的或不足的參數。

(2)cdecl調用慣例保證了參數的正確清除。我們知道有些調用慣例(如stdcall)是由被調用方負責清除堆棧的參數,然而,被調用方在這裡其實根本不知道有多少參數被傳遞進來,所以沒有辦法清除堆棧。而cdecl恰好是調用方負責清除堆棧,因此沒有這個問題。

printf的不定參數比sum要復雜得多,因為printf的參數不僅數量不定,而且類型也不定。所以printf需要在格式字符串中注明參數的類型,例如用%d表明是一個整數。printf裡的格式字符串如果將類型描述錯誤,因為不同參數的大小不同,不僅可能導致這個參數的輸出錯誤,還有可能導致其後的一系列參數錯誤。

  1.  #define va_list char*  
  2.  
  3. #define va_start(ap,arg) (ap=(va_list)&arg+sizeof(arg))  
  4.  
  5. #define va_arg(ap,t) (*(t*)((ap+=sizeof(t))-sizeof(t)))  
  6.  
  7. #define va_end(ap) (ap=(va_list)0)  
  8. printf的狂亂輸出  
  9.  
  10. #include  
  11.  
  12. int main()  
  13.  
  14. {  
  15.  
  16. printf("%lf\t%d\t%c\n", 1, 666, 'a');  
  17.  

在這個程序裡,printf的第一個輸出參數是一個int(4字節),而我們告訴printf它是一個double(8字節以上),因此C++ Sum函數的輸出會錯誤,由於printf在讀取double的時候實際造成了越界,因此後面幾個參數的輸出也會失敗。

在很多時候我們希望在定義宏的時候也能夠像print一樣可以使用變長參數,即宏的參數可以是任意個,這個功能可以由編譯器的變長參數宏實現。在GCC編譯器下,變長參數宏可以使用“##”宏字符串連接操作實現。

  1. C與C++中標准輸入實現方式上的一點區別
  2. C++編譯器如何對Const常量進行分配存儲空間
  3. C++類庫設計的基本構思與方法
  4. 玩轉C++語言的幾種方法
  5. 如何更好的進行C++代碼編制

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved