2016-03-08 72 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'; 
}