1

我有 lineget 函数返回char *(它检测到'\n')和NULLon 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为此目的创建),以便它可以在qsortlike中使用qsort(helper, strtoks, sizeof(char*), compare_string);

广告。2 即使它会起作用 - 我不知道如何清除该行,然后继续对下一行进行排序。怎么做?

我什至使 valgrind 崩溃(使用上面提供的代码)->“valgrind:‘不可能’发生了:被致命信号杀死”

错误在哪里?

4

3 回答 3

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。如果您(或)在 freeing 之后qsort尝试访问,您最终会访问已释放的内存。helper[0]line

您应该改为复制令牌,例如:

ptr = strtok(NULL, " ");
helper[strtoks-1] = malloc(strlen(ptr) + 1);
strcpy(helper[strtoks-1], ptr);

显然,您需要释放helper之后的每个元素(除了helper自身之外)。

于 2012-09-22T22:18:15.257 回答
2

最明显的问题(可能还有其他问题)是您在行首将帮助器重新分配给 strtoks 的值,但随后递增 strtoks 并以更高的 strtoks 值添加到数组中。例如,在第一行,strtoks 为 0,因此temp = realloc(helper, (strtoks)*sizeof(char *));将 helper 保留为NULL,但随后您尝试将该行上的每个单词添加到 helper 数组中。

我会建议一种完全不同的方法,它在概念上更简单:

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
}

我还没有测试过,所以如果它不正确或者有什么你不明白的,请告诉我。我省略了文件的打开和关闭以及最后的释放(请记住,在释放助手本身之前,您必须释放助手的每个元素)。

于 2012-09-22T22:25:43.553 回答
1

您应该收到“Bad alloc”错误,因为:

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()没有返回非 NULL 指针realloc(0, 0),那么我怀疑您没有向我们准确展示导致崩溃的代码valgrind(这是一个公平的成就——恭喜你)因为你没有看到程序在控制下终止,因为它应该realloc(0, 0)返回 NULL。)

如果您使用以下方法,您应该能够避免该问题:

    temp = realloc(helper, (strtoks+1) * sizeof(char *));

不要忘记strtoks在某个时候增加自己。

于 2012-09-22T22:36:19.897 回答