2016-08-24 51 views
1

這裏是我的程序,有誰知道爲什麼?帶有任何參數的C++函數使得分割錯誤

#include <iostream> 
#include <string> 
#include <stdarg.h> 
using namespace std; 

void say(char* name ...) 
{ 
    cout << "Hello, "; 
    cout << name ; 
    va_list ap; 
    va_start(ap,name); 

    for(;;){ 
     char* str = va_arg(ap, char*); 
     if (str == 0) break; 
     cout << ", " << str; 
    } 
    cout << endl; 
    va_end(ap); 
} 
int main() 
{ 
    char *a = "atom"; 
    char *b = "johhny"; 
    char *c = "jim"; 
    say(a,b,c); 
} 

當我運行它,我得到這個:

Program received signal SIGSEGV, Segmentation fault. 
0x0000003f0b06fed0 in strlen() from /lib64/tls/libc.so.6 

這裏是調試信息:

24  cout << "Hello, "; 
(gdb) n 
25  cout << name ; 
(gdb) n 
27  va_start(ap,name); 
(gdb) n 
30   char* str = va_arg(ap, char*); 
(gdb) n 
31   if (str == 0) break; 
(gdb) n 
32   cout << ", " << str; 
(gdb) n 
29  for(;;){ 
(gdb) n 
30   char* str = va_arg(ap, char*); 
(gdb) n 
31   if (str == 0) break; 
(gdb) n 
32   cout << ", " << str; 
(gdb) n 
29  for(;;){ 
(gdb) n 
30   char* str = va_arg(ap, char*); 
(gdb) n 
31   if (str == 0) break; 
(gdb) n 
32   cout << ", " << str; 
(gdb) n 

Program received signal SIGSEGV, Segmentation fault. 
0x0000003f0b06fed0 in strlen() from /lib64/tls/libc.so.6 
(gdb) n 
Single stepping until exit from function strlen, 
which has no line number information. 

Program terminated with signal SIGSEGV, Segmentation fault. 
The program no longer exists. 
+0

'if(str == 0)break;' - 這在實踐中做了什麼? –

+1

下次您在調試器中逐步完成一些代碼,並且知道發生崩潰的時間和位置時,請*查看所涉及的變量。這會爲你節省很多時間。 –

+2

偏題:如果你可以使用C++ 11,看一下可變參數模板。它可以實現完全相同的事情,並具有更高的安全性(您可以傳遞編譯時已知的不同類型,參數數目在編譯時已知,...) – Garf365

回答

2

你的代碼依賴於看到NULL,但主叫方不提供它。

va_arg在您的參數列表結束時不知道。當你的代碼做這個

if (str == 0) break; 

第四次,它檢索一些垃圾過去可變參數列表的末尾,並試圖取消引用它。這是導致未定義行爲的原因,這會導致崩潰。

傳遞明確的解決了這個問題(demo):

say(a,b,c, (char*)0); 
+1

若要擴展傳遞'(char *)0'以結束參數列表:使用'nullptr'代替將一個'nullptr_t'對象放在堆棧上。雖然它可以用來與任何其他指針進行比較,但它本身並不是一個指針,並且實際上與'va_arg(ap,char *)'宏調用不兼容。 –

+0

謝謝@JoachimPileborg:我已經刪除了我的答案。 – Bathsheba

+1

@Bathsheba我來了,我會保留評論,以供將來訪問者參考。 –

3

調用va_arg更多的時間比參數的實際數量是不確定的行爲。您沒有任何自動檢測輸出結束的方法。你必須自己跟蹤有效參數的數量。有多種方法可以做到這一點,例如:

  • 您可以傳遞null或任何特殊值來表示結束。
  • 您可以將參數個數作爲單獨的強制參數傳遞。
  • 您可以維護某種格式的字符串,如printfscanf