2016-03-08 83 views
0
void trim(char *line) 
{ 
    int i = 0; 
    char new_line[strlen(line)]; 
    char *start_line = line; 
    while (*line != '\0') 
    { 
     if (*line != ' ' && *line != '\t') 
     { 
      new_line[i] = *line; 
      i++; 
     } 
     line++; 
    } 
    new_line[i] = '\0'; 
    printf("%s\n", start_line); 
    printf("%s\n", new_line); 
    strcpy(start_line, new_line); 
} 

我真的在這裏找不到問題。我的指針被初始化了,並且我做了一個指針來讓字符串行開始。最後,我想複製舊行中的新行,以便調用者修改行的值。用strcpy分割錯誤,儘管指針有指針

但strcpy()會產生分段錯誤。哪裏不對?

這是調用TRIM()的代碼:

char *str = "Irish People Try American Food"; 
printf("%s\n", str); 
trim(str); 
printf("%s\n", str); 
+1

如果'line'不包含''''或'\ t''會怎麼樣?你的'new_line [i] ='\ 0';'將評估爲'new_line [strlen(line)] ='\ 0';',這是UB。 –

+1

'new_line [strlen(line)];' - >'new_line [strlen(line)+ 1];' – chux

+1

發佈調用'trim(char * line)'的代碼。它是否在做'trim(「Test」);'? – chux

回答

5

你需要顯示整個程序;什麼叫「trim()」?保羅的r答案是正確的,你是一個字符短,它應該至少爲:

char new_line[strlen(line) + 1]; 

然而,這並不總是會導致一個段錯誤,如果沒有的話,大概不會在strcpy()

strcpy(start_line, new_line)爲錯誤的可能原因是start_line指向原始值line。很可能要調用的功能等:

int main() { 
    trim("blah blah\tblah"); 
    return 0; 
} 

如果是這樣,line是一個指向不斷字符數組,不能修改。在許多操作系統上,這些操作都存儲在只讀存儲器區域,因此如果進行寫入嘗試,將導致立即出現分段故障。因此strcpy()嘗試寫入此只讀位置時會出錯。

作爲一個快速測試試試這個:

int main() { 
    char test[100] = "blah blah\tblah"; 
    trim(test); 
    return 0; 
} 

如果一切正常,這就是你用的strcpy()斷層的具體問題。

EDIT - 稍後更新問題以包含main()調用函數,該函數確認使用指向字符串常量的指針調用trim函數。問題行是:

char *str = "Irish People Try American Food"; 

這產生一個字符串,31個字符的陣列,其包括空終止其不能被修改。然後指針str用這個常數的地址初始化,array。

更正是分配一個規則的字符數組,然後使用已知的字符串初始化。在這種情況下,分配和臨時恆定字符串文字可以或可以不被優化的,但最終的結果總是相同的 - 與所需文本初始化字符的可寫數組:

char str[100] = "Irish People Try American Food"; 
/* or */ 
char str2[] = "American People Like Irish Beer"; 
/* or */ 
char *str3[37]; 
strcpy(str3, "And German Beer"); /* example only, need to check length */ 

這些創建正常寫炭長度分別爲100,32和37。然後每個都用給定的字符串進行初始化。

ANSI/ISO C標準定義的語言使得字符串文字是不可修改的char的數組。即使在C89中首次標準化,也是如此。在此之前,字符串文字通常是可寫的,例如在非常早期的UNIX代碼的預標準K & R C中。任一種形式的

相同字符串文字不必是截然不同的。如果 程序試圖修改任一形式的字符串文字,則 行爲未定義。 - ANSI X3.159-1989

許多C89和新的編譯器以來然後置於此陣列到.text或.RODATA段,其中它甚至可以在物理上不可寫(ROM,只讀MMU頁等),正如這裏發現的那樣。編譯器也可能會將重複的字符串常量合併爲一個以節省空間 - 而且您也不會寫入其中的一個!

事實上,這些語義上不可寫入的字符串仍然保留爲char *類型,並且它們可以被分配並傳遞,這被稱爲妥協,即使正在起草C89標準。他們沒有使用當時全新的類型限定符const被描述爲「不完全滿意的結果」。請參閱Richie's (DMR's)說明。

而且很顯然,這一結果仍然存在飛去,近30年後上攻的人敲敲罷了頭。

+0

所以問題是我用'char * test'而不是'char test [100];'。爲什麼第一個是恆定的? – Goldi

+1

@DennisvonEich我更新了答案。基本上是因爲歷史。又一個塵土飛揚的角落。有趣的東西,但。 – Anders

+0

我相當贊成這個話題太複雜了。 – Goldi

6

new_line字符串是一個字符太小了 - 它並沒有足夠的空間用於最終'\0'終結者 - 變化:

char new_line[strlen(line)]; 

到:

char new_line[strlen(line) + 1]; 

你也應該知道,字符串公升ALS不能修改,所以如果你試圖調用你的函數是這樣的:

trim("Hello world!"); 

,那麼這將導致不確定的行爲。 (如果你嘗試這樣做,你也應該得到一個編譯器警告。)

+0

謝謝,但在mos的情況下,字符串有空格和\ t這意味着它比'char * line'參數小。我試了一下,但沒有奏效。 – Goldi

+2

@DennisvonEich:好吧,這是一個需要修復的錯誤 - 也可能有其他錯誤。 –

4

正如@PaulR所說,你的新行的緩衝區太小了。但是,而不是使用另一個緩衝區,佔用了更多的空間,你可以使用一個單字符的方法,如:

void trim(char *s) 
{ 
    char *src = s, *dest = s; 
    while (*src) 
    { 
     if ((*src != ' ') && (*src != '\t')) 
      *dest++ = *src; 
     ++src; 
    } 
    *dest = '\0'; 
}