这个c函数有什么问题?

时间:2011-01-30 15:50:10

标签: c

以下函数必须将一条线分成2行或更多行,每行都比s短。

char **splitline(FILE *fp, int s)
{
    char **l;
    char c;
    int ccounter;
    int lcounter;

    c = fgetc(fp);
    if (c == EOF)
    {
        return NULL;
    }

    lcounter=0;
    l = malloc(sizeof(char **));
    l[lcounter] = malloc((SIZE+2)*sizeof(char));

    ccounter = 0;
    while (c != EOF && c != '\n')
    {
        l[lcounter][ccounter] = c;
        ccounter++;
        c = fgetc(fp);

        if (ccounter == SIZE)
        {
            l[lcounter][ccounter] = '\n';
            ccounter++;
            l[lcounter][ccounter] = '\0';

            realloc(l, (lcounter+2) * sizeof(char **));

            lcounter++;

            l[lcounter] = malloc((SIZE+2) * sizeof(char));
            ccounter = 0;
        }
    }

    if (ccounter == 0)
    {
        l[lcounter][ccounter] = '\0'; 
    }
    else
    {
        l[lcounter][ccounter] = '\n';
        ccounter++;
        l[lcounter][ccounter] = '\0';

        realloc(l, (lcounter+2) * sizeof(char **));

        lcounter++;

        ccounter = 0;
        l[lcounter] = malloc((SIZE+2) * sizeof(char));
        l[lcounter][ccounter] = '\0';
    }

    return l;
}

2 个答案:

答案 0 :(得分:5)

  • 您正在使用未定义的常量SIZE而不是参数s来控制行的最大长度。由于s是一个有符号整数,你应该检查它是否合理(它应该是正数(非零)数字,可能不大于1 MiB;也许你想设置一个比1更大的下限;也许你如果呼叫者搞砸了,则默认为80,或者您可能会返回错误)。

  • 您正在使用char c;保存使用fgetc()读取的值;遗憾的是,这意味着您的EOF测试不可靠。当有人提供'ÿ'(y-umlaut,ISO 8859-1中的十六进制0xFF,ISO 8646中的U + 00FF - Unicode)或者根本不会停止时,你要么提前停止类型char是签名还是未签名。永远记住:getchar()和亲戚返回int

  • 变量l正在引发与常量1的混淆;一般都要避免它。

  • 如果直接测试fgetc()的结果,主循环条件会更好。

    int c;
    while ((c = fgetc(fp)) != EOF && c != '\n')
    {
        ...
    }
    

    实际上,你在循环中读取了一个字符并且没​​有正确检查它。然后,您可以使用ungetc()将字符首先读回输入流;它使输入处理更加均匀。或者,如果第一次调用fgetc()在循环控件中并且它返回EOF,您可以进行设置以便一切正常。

  • 正如Tommy所指出,您必须捕获realloc()的输出;无法保证它会返回其输入指针作为结果。您现在还应该了解到,不将realloc()的结果保存到指定为其第一个参数的变量中。如果你这样做,你会立即泄漏内存并且重新分配失败(因为你丢失了指向旧内存的指针 - 它只是归零)。所以,为了安全起见,请使用:

    char **new_array = realloc(l, (lcounter+1) * sizeof(*new_array));
    if (new_array == 0)
        ...handle out of memory...
    else
        l = new_array;
    

    这里有几点。您分配了(lcounter+2)个值,但我认为您只使用其中一个(除非有一个终端空指针标记数组的结尾)。您指定了sizeof(char **),但实际上您需要一组char *值。幸运的是,所有(对象)指针的大小都相同--C标准保证;只有POSIX保证函数指针与对象指针的大小相同(C标准没有)。所以,sizeof(char **) == sizeof(char *)你是安全的,但你不是在问你想要什么。

  • 有关realloc()可能失败的讨论的必然结果malloc()也可能失败。您应该错误检查内存分配 - 或者使用标准库的一组封面函数,只有在返回的指针不为空时才返回。如果你不检查它,你的程序最终会因内存分配失败而崩溃 - 即使在具有24 GiB主内存的机器上(尽管可能需要一段时间才能达到这一点)。

  • 代码中有很多重复。你应该避免这样做。这意味着可能使用子功能来管理内存分配。

如果你解决这些问题,那么你就有机会获得正常工作。您还应编写代码以释放您返回的已分配内存,以便用户无需为您设计方法。总是担心谁会释放分配的内存以及它将如何发布。


Chris Lutz问道:

  

标准在哪里保证所有对象指针类型的大小都相同?

我认为(C99-ISO / IEC 9899:1999)标准的相关部分是:

  

§6.3.2.3指针

     

1指向void的指针可以转换为指向任何不完整或对象的指针   类型。指向任何不完整或对象类型的指针可以转换为指向void的指针   又回来了;结果应该等于原始指针。

这基本上说所有对象指针类型都可以转换为void指针并再次返回而不会丢失信息,这意味着它们必须都是相同的大小。请注意,“对象指针”类别不包含“函数指针”。

POSIX标准的相关部分(关于函数指针)是§2.12.3(在链接部分的底部)。

答案 1 :(得分:4)

我能发现的主要内容:realloc可能无法将缓冲区保留在同一地址。你应该使用

 l = realloc(...)

实际上,请参阅下面Matteo的评论。 Realloc可以做以下三件事之一:

  • 扩展你已经拥有的缓冲区的大小(在这种情况下,你会得到相同的指针);
  • 在其他位置分配一个新缓冲区,复制缓冲区内容并释放原始内容(在这种情况下,您将获得不同的指针);
  • 发现自己无法在任何地方找到所需大小的缓冲区并失败,使您当前的内容保持不变(返回NULL)。

在最后一种情况下,不检查返回值的赋值会导致内存泄漏。