动态创建C字符串

时间:2013-11-17 15:16:51

标签: c arrays string realloc

我正在尝试从用户处获取表达式并将其放入动态创建的字符串中。这是代码:

char *get_exp() {
    char *exp, *tmp = NULL;
    size_t size = 0;
    char c;
    scanf("%c", &c);

    while (c != EOF && c != '\n') {
        tmp = realloc(exp, ++size * sizeof char);
        if (tmp == NULL)
            return NULL;

        exp = tmp;
        exp[size-1] = c;
        scanf("%c", &c);
    }
    tmp = realloc(exp, size+1 * sizeof char);
    size++;
    exp = tmp;
    exp[size] = '\0';
    return exp;
}

但是,由于某种原因,每次读取的第一个字符都是换行符,因此while循环退出。我正在使用XCode,可能是导致问题的原因吗?

3 个答案:

答案 0 :(得分:1)

不,XCode不是你问题的一部分(这是一个谴责他的工具的穷人)。

您尚未初始化exp,这会导致问题。

检测EOF的代码完全被破坏了;您必须测试scanf()的返回值以检测EOF。使用getchar() int c时,您可以做得更好:

int c;

while ((c = getchar()) != EOF && c != '\n')
{
    ...
}

如果您认为必须使用scanf(),那么您需要测试对scanf()的每次通话:

char c;

while (scanf("%c", &c) == 1 && c != EOF)
{
    ...
}

你确实在循环中检查realloc()的结果;这很好。在循环之后你不会检查realloc()的结果(并且你没有缩减你的分配);请每次检查。

您应该考虑使用一次分配多个字节的机制,而不是每个字符读取一个realloc();这很贵。

当然,如果目标只是读取一行,那么最简单的方法是使用POSIX getline(),它会为你处理所有的分配。或者,您可以使用 fgets()阅读该行。您可以使用固定缓冲区来收集数据,然后将其复制到适当大小的动态分配缓冲区。您还可以考虑线路很长的可能性,以便检查您是否确实获得了新线路。

答案 1 :(得分:0)

在Windows XP / cc上,就像迈克尔所说,如果将exp初始化为NULL,它就可以工作。

答案 2 :(得分:0)

这是一个固定的代码,其中的注释解释了与问题中的代码不同的内容:

char *get_exp()
{
    // keep variables with narrowest scope possible
    char *exp = NULL;
    size_t size = 0;

    // use a "forever" loop with break in the middle, to avoid code duplication
    for(;;) {

        // removed sizeof char, because that is defined to be 1 in C standard
        char *tmp = realloc(exp, ++size);
        if (tmp == NULL) {
            // in your code, you did not free already reserved memory here
            free(exp); // free(NULL) is allowed (does nothing)
            return NULL;
        }
        exp = tmp;

        // Using getchar instead of scanf to get EOF,
        // type int required to have both all byte values, and EOF value.
        // If you do use scanf, you should also check it's return value (read doc).
        int ch = getchar();
        if (ch == EOF) break; // eof (or error, use feof(stdin)/ferror(stdin) to check)
        if (ch == '\n') break; // end of line
        exp[size - 1] = ch; // implicit cast to char
    }

    if (exp) {
        // If we got here, for loop above did break after reallocing buffer,
        // but before storing anything to the new byte.
        // Your code put the terminating '\0' to 1 byte beyond end of allocation.
        exp[size-1] = '\0';
    }
    // else exp = strdup(""); // uncomment if you want to return empty string for empty line
    return exp;
}