我有一个lineget函数,它在char *
上返回'\n'
(它检测到NULL
)和EOF
。
在main()
中,我试图识别该行中的特定单词。
我用了strtok:
int main(int argc, char **argv)
{
char *line, *ptr;
FILE *infile;
FILE *outfile;
char **helper = NULL;
int strtoks = 0;
void *temp;
infile=fopen(argv[1],"r");
outfile=fopen(argv[2],"w");
while(((line=readline(infile))!=NULL))
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if(temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
} else {
helper=temp;
}
while (ptr != NULL) {
strtoks++;
fputs(ptr, outfile);
fputc(' ', outfile);
ptr = strtok(NULL, " ");
helper[strtoks-1] = ptr;
}
/*fputs(line, outfile);*/
free(line);
}
fclose(infile);
fclose(outfile);
return 0;
}
现在我不知道如何将每个标记化的单词放入一个数组中(我为此目的创建了char ** helper
),以便可以在qsort
qsort(helper, strtoks, sizeof(char*), compare_string);
中使用它。
广告。 2即使它可以工作 - 我不知道如何清除该行,并继续排序下一行。怎么做?
我甚至崩溃了valgrind(使用上面提到的代码) - > “valgrind:'不可能'发生了: 被致命信号杀死“
错误在哪里?
答案 0 :(得分:2)
正如您在strtok
的原型中所看到的那样:
char * strtok ( char * str, const char * delimiters );
... str
不是const
。 strtok
实际上做的是将找到的分隔符替换为空字节(\0
)到str
中并返回指向令牌开头的指针。
每个例子:
char in[] = "foo bar baz";
char *toks[3];
toks[0] = strtok(in, " ");
toks[1] = strtok(NULL, " ");
toks[2] = strtok(NULL, " ");
printf("%p %s\n%p %s\n%p %s\n", toks[0], toks[0], toks[1], toks[1],
toks[2], toks[2]);
printf("%p %s\n%p %s\n%p %s\n", &in[0], &in[0], &in[4], &in[4],
&in[8], &in[8]);
现在看结果:
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
如您所见,toks[1]
和&in[4]
指向同一位置:原始str
已被修改,实际上toks
中的所有令牌都指向某处在str
。
在您的情况下,您的问题是您可以免费line
:
free(line);
...使helper
中的所有指针无效。如果您(或qsort
)在释放helper[0]
后尝试访问line
,则最终会访问已释放的内存。
您应该复制代币,例如:
ptr = strtok(NULL, " ");
helper[strtoks-1] = malloc(strlen(ptr) + 1);
strcpy(helper[strtoks-1], ptr);
显然,之后你需要释放helper
的每个元素(除了helper
本身)。
答案 1 :(得分:2)
最明显的问题(可能还有其他问题)是你在行的开头将helper重新分配给strtoks的值,然后递增strtoks并以更高的strtoks值添加到数组中。例如,在第一行,strtoks为0,因此temp = realloc(helper, (strtoks)*sizeof(char *));
将帮助器保留为NULL
,但是您尝试将该行上的每个单词添加到辅助数组。
我建议采用一种完全不同的方法,这种方法在概念上更简单:
char buf[1000]; // or big enough to be bigger than any word you'll encounter
char ** helper;
int i, numwords;
while(!feof(infile)) { // most general way of testing if EOF is reached, since EOF
// is just a macro and may not be machine-independent.
for(i = 0; (ch = fgetc(infile)) != ' ' && ch != '\n'; i++) {
// get chars one at a time until we hit a space or a newline
buf[i] = ch; // add char to buffer
}
buf[i + 1] = '\0' // terminate with null byte
helper = realloc(++numwords * sizeof(char *)); // expand helper to fit one more word
helper[numwords - 1] = strdup(buffer) // copy current contents of buffer to the just-created element of helper
}
我没有对此进行过测试,所以请告诉我这是不正确的还是你有什么不明白的。我已经省略了文件的打开和关闭以及最后的释放(记住你必须在释放帮助器之前释放帮助器的每个元素)。
答案 2 :(得分:1)
您应该收到“错误的分配”错误,因为:
char **helper = NULL;
int strtoks = 0;
...
while ((line = readline(infile)) != NULL) /* Fewer, but sufficient, parentheses */
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if (temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
}
这是因为strtoks
的值为零,所以你要求realloc()
释放helper
指向的内存(它本身就是一个空指针)。一个偶然的机会是你的图书馆在realloc(0, 0)
上崩溃了,不应该这样,但这是一个可能被忽视的好奇边缘案例。另一种可能性是realloc(0, 0)
返回一个非空指针,指向0字节的数据,不允许取消引用。当您的代码取消引用它时,它会崩溃。 C标准允许返回NULL和返回非NULL;不管编译器realloc()
显示哪种行为,都不要编写崩溃的代码。 (如果realloc()
的实现没有为realloc(0, 0)
返回非NULL指针,那么我怀疑您没有向我们展示设法崩溃的代码valgrind
(这是一个公平的成就 - 祝贺你,因为如果realloc(0, 0)
返回NULL,你没有看到程序终止受控制。)
如果使用:
,您应该可以避免该问题 temp = realloc(helper, (strtoks+1) * sizeof(char *));
不要忘记在某个时候增加strtoks
本身。