如何将char *放入数组,以便我可以在qsort中使用它,然后继续前进到下一行

时间:2012-09-22 21:43:56

标签: c pointers qsort

我有一个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:'不可能'发生了:    被致命信号杀死“

错误在哪里?

3 个答案:

答案 0 :(得分:2)

正如您在strtok的原型中所看到的那样:

char * strtok ( char * str, const char * delimiters );

... str不是conststrtok实际上做的是将找到的分隔符替换为空字节(\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本身。