奇怪的分段错误第二次在相同的字符串C上执行strcpy时出错

时间:2017-11-29 02:01:40

标签: c strcpy fault

我正在用逗号分隔的行标记,并且因为一些标题中包含逗号,所以它们被引号括起来。我将第一个引号标记为正确,将其存储在前面,然后直到第二个引用,将其存储到标题中,然后最后我将其标记为\ n。我不明白为什么。

是否与strsep有关?我没有使用strtok因为我想要捕获空标记,如果字符串由“,,”多个分隔符组成,它们之间没有任何内容。

由于tempStr已经超过了必要的数量,因此它应该足以作为strcpy的* dest。我被困在这几个小时。如果有人能指出我的错误,我将非常感激。谢谢。

int main(int argc, char * argv[])
{   
    char* one = "hello, my, name, is, code monkey, \"This, is a title\", more, random, stuff\n";
    char* two = "blah blah blah";

    char* tempStr= malloc(1000);
    void* freeTempStr = tempStr;

    strcpy(tempStr, one);

    char* fronttoken = strsep(&tempStr, "\"");
    char* title = strsep(&tempStr, "\"");
    char* backtoken = strsep(&tempStr, "\n");
    char* token;

    strcpy(tempStr, fronttoken);
    token = strsep(&tempStr, ",");
    while (token != NULL)
    {
        printf("Front tokens: %s\n", token);
        token = strsep(&tempStr, ",");
    }
    printf("Title: %s\n", title);
    strcpy(tempStr, backtoken);
    token = strsep(&tempStr, ",");
    while (token != NULL)
    {
        printf("Back tokens: %s\n", token);
        token = strsep(&tempStr, ",");
    }
    //2nd strcpy gives segmentation fault

    free(freeTempStr)
    return 0;
}
  

...输出

Front tokens: hello
Front tokens:  my
Front tokens:  name
Front tokens:  is
Front tokens:  code monkey
Front tokens:
Title: This, is a title
Segmentation fault

2 个答案:

答案 0 :(得分:0)

在对代码进行了更深入的研究并对其进行编译和实验后,我同意M.Manalysis,即时问题在于你是 试图复制到空指针。

但是,我怀疑你没有仔细考虑内存管理。执行strcpy(tempStr, one);时,将源字符串复制到已分配的内存中。 (联合国)幸运的是,你分配的内存比所需的副本多得多。当您随后执行strcpy(tempStr, fronttoken);时,您正在将fronttoken复制到onetempStr的原始副本之后的位置。然后你把它分开了。

您崩溃是因为当tempStr设置为null时停止,然后您尝试strcpy(tempStr, backtoken),复制到空指针。

如果你解决了这个问题,你可能会遇到重叠字符串副本的问题。你当前的一组反向令牌足够小,不会有问题,但如果你有一个100字节的反向令牌,你就会有一个重叠的字符串副本和未定义的行为。

此代码显示问题并修复它。请注意,它包含标记周围的方括号,因此更容易确切地看到找到的内容。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{   
    char* one = "hello, my, name, is, code monkey, \"This, is a title\", more, random, stuff\n";

    char* tempStr= malloc(1000);
    void* freeTempStr = tempStr;

    strcpy(tempStr, one);
    printf("tempStr = [%p .. %p)\n", (void *)tempStr, (void *)(tempStr + 1000));

    char* fronttoken = strsep(&tempStr, "\"");
    printf("tempStr = %p; fronttoken = %p\n", (void *)tempStr, (void *)fronttoken);
    char* title = strsep(&tempStr, "\"");
    printf("tempStr = %p; title = %p\n", (void *)tempStr, (void *)title);
    char* backtoken = strsep(&tempStr, "\n");
    printf("tempStr = %p; backtoken = %p\n", (void *)tempStr, (void *)backtoken);
    char* token;

    printf("tempStr = %p; fronttoken = %p - before strcpy 1\n", (void *)tempStr, (void *)fronttoken);
    strcpy(tempStr, fronttoken);
    token = strsep(&tempStr, ",");
    while (token != NULL)
    {
        printf("Front tokens: %p [%s]\n", (void *)token, token);
        token = strsep(&tempStr, ",");
    }
    printf("Title: [%s]\n", title);
    printf("tempStr = %p; backtoken = %p - before strcpy 2 (unfixed)\n", tempStr, backtoken);
    tempStr = freeTempStr;
    printf("tempStr = %p; backtoken = %p - before strcpy 2 (fixed - but beware overlap)\n", tempStr, backtoken);
    strcpy(tempStr, backtoken);
    token = strsep(&tempStr, ",");
    while (token != NULL)
    {
        printf("Back tokens: %p [%s]\n", (void *)token, token);
        token = strsep(&tempStr, ",");
    }

    free(freeTempStr);
    return 0;
}

示例输出(Mac运行macOS High Sierra 10.13.1,安装了安全更新2017-001 - macOS 10.13.1 (17B1002) - 如果您还没有安装它,请安装它!):

tempStr = [0x7fb91ac02880 .. 0x7fb91ac02c68)
tempStr = 0x7fb91ac028a3; fronttoken = 0x7fb91ac02880
tempStr = 0x7fb91ac028b4; title = 0x7fb91ac028a3
tempStr = 0x7fb91ac028ca; backtoken = 0x7fb91ac028b4
tempStr = 0x7fb91ac028ca; fronttoken = 0x7fb91ac02880 - before strcpy 1
Front tokens: 0x7fb91ac028ca [hello]
Front tokens: 0x7fb91ac028d0 [ my]
Front tokens: 0x7fb91ac028d4 [ name]
Front tokens: 0x7fb91ac028da [ is]
Front tokens: 0x7fb91ac028de [ code monkey]
Front tokens: 0x7fb91ac028eb [ ]
Title: [This, is a title]
tempStr = 0x0; backtoken = 0x7fb91ac028b4 - before strcpy 2 (unfixed)
tempStr = 0x7fb91ac02880; backtoken = 0x7fb91ac028b4 - before strcpy 2 (fixed - but beware overlap)
Back tokens: 0x7fb91ac02880 []
Back tokens: 0x7fb91ac02881 [ more]
Back tokens: 0x7fb91ac02887 [ random]
Back tokens: 0x7fb91ac0288f [ stuff]

请注意,使用调试器还可以让您很快找到此问题。

答案 1 :(得分:0)

您的代码未编译为已发布;有一个缺少分号。此外,缺少许多标准标题。

在带有gcc 6.2.6的64位Ubuntu 16上,除了你观察它之外发生了崩溃。由于缺少<string.h>标头,strsep函数被隐式声明为返回int,这是错误的。因此,fronttoken变量收到垃圾值,第一个strcpy失败。

要做的第一件事是获得一个没有错误或警告的干净构建(在打开编译器用户社区广泛推荐的任何诊断之后)。

修复所有问题之后,您会遇到一个简单的逻辑问题:

while (token is not null) {
   token = strsep(&tempStr, ...)

}

strcpy into tempStr

由于while循环不包含break语句,因此它终止的唯一方法是token变为null。

token变为空的唯一方法是strsep返回null。

根据文档,strsep返回null的唯一方法是tempStr为空。

即。 token为空的事实证明tempStr必须为空,这意味着我们不得将tempStr用作strcpy的目的地。

tempStr如何变为空是在之前的strsep调用中,未找到任何标记分隔符。在这种情况下,strsep将整个字符串作为提取的标记返回,并用null覆盖指针。

换句话说,在从字符串中提取最后一个标记后,strsep将使用null覆盖指针。然后在下一次调用strsep时,它返回null,表示“没有更多令牌可用”。这使得strsep易于使用:只要一直调用它,直到你得到null。但是你必须明白临时上下文指针在这个过程中被清空了。