我正在用逗号分隔的行标记,并且因为一些标题中包含逗号,所以它们被引号括起来。我将第一个引号标记为正确,将其存储在前面,然后直到第二个引用,将其存储到标题中,然后最后我将其标记为\ 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
答案 0 :(得分:0)
在对代码进行了更深入的研究并对其进行编译和实验后,我同意M.M的analysis,即时问题在于你是 试图复制到空指针。
但是,我怀疑你没有仔细考虑内存管理。执行strcpy(tempStr, one);
时,将源字符串复制到已分配的内存中。 (联合国)幸运的是,你分配的内存比所需的副本多得多。当您随后执行strcpy(tempStr, fronttoken);
时,您正在将fronttoken
复制到one
中tempStr
的原始副本之后的位置。然后你把它分开了。
您崩溃是因为当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。但是你必须明白临时上下文指针在这个过程中被清空了。