【问题标题】:Input string without knowing the size在不知道大小的情况下输入字符串
【发布时间】:2018-03-24 04:26:14
【问题描述】:

当我想存储我不知道大小的字符串时,该怎么办。

我喜欢这样:

#include <stdio.h>    
#include <conio.h>

int main () {
    char * str;
    str = (char *)malloc(sizeof(char) + 1);
    str[1] = '\0';
    int i = 0;
    int c = '\0';

    do {
        c = getche();
        if(c != '\r'){
            str[i] = c;
            str[i + 1] = '\0';
            i++;
            str = (char *)realloc(str, sizeof(char) + i + 2);
        }
    } while(c != '\r');

    printf("\n%s\n", str);

    free(str);
    return 0;
}

我找到了这个页面: Dynamically prompt for string without knowing string size

正确吗?如果是,那么:

有没有更好的办法?

有没有更有效的方法?

【问题讨论】:

  • conio.h 是非标准的。
  • 请阅读并理解the question on why not to cast the return value of malloc() and family in C。另请注意,根据定义,sizeof (char) 是一,因为sizeofchar 为单位给出其结果。
  • "正确吗?" - 最好自己确定。创建一些测试用例来解决可能出现的问题(例如空输入、很长的输入等)。一旦您确定它的功能符合您的规范,您可能想通过Code Review 寻求批评。请务必先阅读A guide to Code Review for Stack Overflow users,因为那里有些事情的处理方式不同!
  • "有没有更好的办法?" IMO,允许用户消耗无限的内存资源,因为这种方法尝试生成邀请黑客的代码。最好对字符串输入长度有一个合理的有限上限。
  • 对于 do...while() 循环,将所有内容替换为对 readline() 的调用。该函数将从堆中为整行分配足够的内存,并返回指向堆中分配区域的指针。 (如果分配失败,则为 NULL)

标签: c string performance pointers memory-management


【解决方案1】:

对吗?

主要问题是realloc的使用。这是错误的。使用realloc 时,切勿直接分配指向已分配内存的指针 - 始终使用临时值来获取返回值。喜欢:

char * temp;
temp = realloc(str, 1 + i + 2);
if (temp == NULL)
{
     // out of memory
     .. add error handling
}
str = temp;

这样做的原因是realloc 可能会失败,在这种情况下它将返回 NULL。因此,如果您直接分配给str 并且realloc 失败,您就丢失了指向已分配内存的指针(也就是字符串)。

除此之外:

1) 不要投射 mallocrealloc

2) sizeof(char) 总是 1 - 所以你不需要使用它 - 只需输入 1

有没有更好的方法? 有没有更有效的方法?

与其在每个循环中重新分配 1 - 这在性能方面非常昂贵 - 在许多情况下,最好(重新)分配一个更大的块。

一种策略是在调用realloc 时将分配加倍。因此,如果您分配了 128 个字节,那么下一个分配应该是 2*128=256。另一种策略是让它以远大于 1 的固定大小增长 - 例如,您可以让它每次增长 1024。

【讨论】:

    【解决方案2】:

    我建议使用缓冲区来避免重复的 realloc 调用。创建缓冲区或任意大小,例如1024 当它填满时,您可以将更多空间重新分配给动态分配的缓冲区并将缓冲区内存移动到其中。

    【讨论】:

      【解决方案3】:

      回答这个问题的关键是澄清术语“不知道大小”

      我们可能不知道我们将获得多少数据,但我们可能知道我们将如何处理这些数据。

      让我们考虑以下用例:

      • 我们对我们需要的数据有限制,例如:人名、地址、书名。我想我们最好有 1k 或最多 16k 的空间。

      • 我们获得连续的数据流,例如:某些传感器或其他设备每秒向我们发送数据。在这种情况下,我们可以分块处理数据。

      回答

      • 我们需要对我们打算处理的大小做出有根据的猜测并相应地分配空间。
      • 我们必须动态处理数据,我们需要释放不再需要的空间。

      注意: 需要注意的是,我们不能分配无限大小的内存。在某些时候,我们必须实现错误处理和/或我们需要将数据存储在“磁盘”或其他地方。

      注意事项二: 如果需要更高效的内存解决方案,不推荐使用realloc,因为它可以复制分配的大小(如果系统不能简单地增加分配的空间,它首先分配一个新的内存块并复制当前内容)而跑步。相反,将需要特定于应用程序的内存结构。但我认为这超出了原始问题的范围。

      【讨论】:

        【解决方案4】:

        对吗?

        有点。

        We don't cast the result of malloc() in C.

        有没有更好的办法?

        这主要是基于意见的。

        有没有更有效的方法?

        关于时间还是空间?

        如果你问的是空间,没有。

        如果你问的是时间,是的。

        您可以为一个小尺寸的数组动态分配内存,这样可以将字符串保存一段时间。然后,当数组无法再保存字符串时,您将重新分配该内存并将其大小加倍。依此类推,直到读取整个字符串。完成后,您可以再次重新分配内存,并将大小缩小到字符串所需的确切数字。

        您知道,调用realloc() 的时间成本很高,因为它可能必须移动整个内存块,因为内存必须是连续的,并且在不移动内存的情况下可能没有剩余空间来执行该操作与字符串有关。


        注意:当然,静态创建的固定大小的数组在时间方面会更好,但在内存方面会更糟。一切都是一种权衡,这就是您发挥作用并决定最适合您的应用程序的地方。

        【讨论】:

        • 存储分配块的数组或链接存储结构可以提供更节省空间的解决方案,而不是一直重新分配。但我们可能会处理数据而不是简单地存储它!
        • 显然是@SchLx,但我怀疑这是 OP 的缩进。
        • 我只是想指出,“如果你问的是空间,不。”这个答案并不完全正确!
        • 最有效的方法是静态分配char str [FRICKEN_BIG_ARRAY]; 并将数据存储在那里。无论如何,我认为您准确地解决了为什么不应该首先回答这个问题的所有原因。
        【解决方案5】:

        这个怎么样?

        char *string_name;
        asprintf(&string_name, "Hello World, my name is %s & I'm %d years old!", "James Bond", 27);
        printf("string is %s", string_name);
        free(string_name);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-11-23
          • 1970-01-01
          • 2022-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-14
          • 1970-01-01
          相关资源
          最近更新 更多