strcpy的分段错误,即使指针有指针对象

strcpy的分段错误,即使指针有指针对象,c,segmentation-fault,C,Segmentation Fault,我真的找不到这里的问题。我的指针已初始化,我创建了一个指针,以获取字符串行的开头。最后,我想把新行复制到旧行中,这样调用者的行的值就改变了 但是strcpy有一个分段错误。怎么了 这是调用trim的代码: void trim(char *line) { int i = 0; char new_line[strlen(line)]; char *start_line = line; while (*line != '\0') { if (*l

我真的找不到这里的问题。我的指针已初始化,我创建了一个指针,以获取字符串行的开头。最后,我想把新行复制到旧行中,这样调用者的行的值就改变了

但是strcpy有一个分段错误。怎么了

这是调用trim的代码:

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);
}
您的新行字符串太小了一个字符-它没有空间容纳最后的“\0”终止符-更改:

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

您还应注意,字符串文字不能修改,因此,如果您尝试像这样调用函数:

char new_line[strlen(line) + 1];
trim("Hello world!");

这将导致未定义的行为。如果您尝试这样做,还应该得到一个编译器警告。

正如@PaulR所述,新行的缓冲区太小。但是,您可以使用单字符方法,而不是使用另一个占用更多空间的缓冲区,如下所示:

char new_line[strlen(line) + 1];
trim("Hello world!");

你需要展示整个节目;什么叫修剪?Paul R的回答是对的,你只缺一个字符,至少应该是:

void trim(char *s)
{
    char *src = s, *dest = s;
    while (*src)
    {
        if ((*src != ' ') && (*src != '\t'))
            *dest++ = *src;
        ++src;
    }
    *dest = '\0';
}
然而,这并不总是会导致segfault,如果它发生,则可能不会发生在strcpy

strcpystart_线、new_线出现故障的可能原因是start_线指向线的原始值。您很可能正在调用以下函数:

char new_line[strlen(line) + 1];
如果是,则line是指向无法修改的常量字符数组的指针。在许多操作系统上,这是存储在只读存储器区域中的,因此,如果尝试写入,将立即导致分段错误。因此,strcpy在尝试写入此只读位置时会出现故障

作为快速测试,请尝试以下方法:

int main() {
    trim("blah blah\tblah");
    return 0;
}
如果它起作用,那就是strcpy断层的具体问题

编辑-该问题稍后更新为包含主调用函数,该函数确认使用指向字符串常量的指针调用了trim函数。问题是:

int main() {
    char test[100] = "blah blah\tblah";
    trim(test);
    return 0;
}
这将创建一个字符串文字,一个包含31个字符的数组,其中包含一个不能修改的空终止符。然后用这个常量数组的地址初始化指针str

纠正方法是分配一个规则的字符数组,然后用已知字符串初始化它。在这种情况下,赋值和临时常量字符串文字可能会被优化,也可能不会被优化,但最终结果总是相同的——一个用所需文本初始化的可写字符数组:

char *str = "Irish People Try American Food";
它们分别创建长度为100、32和37的普通可写字符数组。然后用给定的字符串初始化每个字符串

ANSI/ISO C标准定义了该语言,使得字符串文字是一个不能修改的字符数组。即使在C89首次标准化时也是如此。在此之前,字符串文本通常是可写的,例如在早期UNIX代码的标准K&R C中

两种形式的相同字符串文本不必不同。如果 该程序尝试修改任意形式的字符串文字 行为没有定义ANSI X3.159-1989

此后,许多C89和更新的编译器将此数组放入.text或.rodata段,在这些段中,它甚至可能是物理上不可写入的ROM、只读MMU页等,如本文所示。编译器还可以将重复的字符串常量合并为单个常量以节省空间,而且您也不需要写入其中的任何一个

事实上,这些语义上不可写的字符串仍然保留为char*类型,并且它们可以这样分配和传递,这是一种折衷,即使C89标准正在起草中。他们没有使用当时全新的限定词const被描述为一个不完全令人满意的结果。见解释


显然,这一结果仍然会在近30年后卷土重来,让人头晕目眩。

如果线条中不包含“”或“\t”怎么办?你的新行[i]='\0';将计算为新行[strlenline]='\0';,哪一条是UB.new_line[strlenline];->新线[strlenline+1];发布调用trimchar*行的代码。它是不是在做一些修剪测试@chux现在已更新。代码正在尝试修改字符串文字爱尔兰人尝试美国食品。->乌兰巴托。许多重复的字符串,但在mos情况下,字符串有空格和\t,这意味着它小于char*line参数。我试过了,但没用。@DennisvonEich:这是一个需要修复的bug——可能还有其他bug。所以问题是我使用了char*test;而不是字符测试[100];。为什么第一个是常数?@DennisvonEich我更新了答案。基本上是因为历史。只是又一个尘土飞扬的角落。有趣的东西,虽然。我相当支持这个话题是如此复杂。