经常重新分配内存来处理字符串是一个好的做法吗?

3
我有一个格式化字符串,正在解析它并将格式说明符替换为输入参数。现在我在考虑如何为替换参数后的结果字符串分配内存。我可以将该字符串分配为与格式化字符串一样长,但是将任何长字符串的%s替换为其他字符串,则需要以某种不确定的方式重新分配此字符串,这会导致代码中出现一些不雅的计算。因此,我想我可以按字符逐个分配此由格式化字符串创建的字符串,每次重新分配它,例如:
/*** for loop traversing next chars in format string ***/
// if new char 
str = realloc(str, sizeof(*str) +1); 
// if %s 
str = realloc(str, sizeof(*str) + strlen(in_str)); 
// if %d 
str = realloc(str, sizeof(*str) + strlen(d_str)); 

6
这些陈述似乎是不正确的:sizeof(*str)不是到目前为止分配的字符串的大小。 - chqrlie
你可以创建一个轻量级的字符串类(对于C语言,是一个“结构体”),它包含一个当前的“max_length”(最初应该是“大致足够大多数情况”)。然而,这会增加大约相同的开销,因为你总是需要在添加之前检查最大长度。 - Jongware
4
str = realloc(str, 这个模式不允许从内存不足中恢复。 - M.M
这是一个不好的想法(除了已经暗示的错误)。如果您对字符串大小有一个想法(比如“它永远不会超过160、200、300个字符”),最好分配一个静态缓冲区,而不是不断地打扰操作系统进行内存分配调用。根据系统的不同,内存浪费可能比操作系统开销更容易被容忍。 - tofro
2
你是否遇到了可以通过减少字符串重新分配来解决的性能问题?如果是,那么这很糟糕,但你可以解决它。如果你没有性能问题,就不要担心调用realloc()的次数。 - Andrew Henle
3个回答

6
通常处理可变字符串/数组/列表等长度的内部库代码,都是采用2的n次幂的方式,即当您需要分配5个字节的内存时,在实际分配时会分配8个字节。这将减少realloc()调用的次数,因为这是非常昂贵的操作,只需进行~log(n)次操作即可。 但是,根据库的不同,还可能有其他的优化方法。

4
我不会对在其他答案中已经得到充分解决的代码问题发表评论。
每次扩展字符串都调用realloc并不一定是一种糟糕的做法。看起来这样做可能会导致性能下降,为了解决这个问题,你可以实现一种方案,按较大的增量较少地频繁增加字符串长度。然而,你怎么知道realloc内部是否已经执行了类似的操作呢?例如,你可能认为通过将128字节的字符串增长到256字节,然后再增长到512字节,而不是逐个字符增加,这样做很聪明。但是,如果这些恰好是唯一可用的malloc内部块大小,那么realloc只能通过相同的大小步进。好吧,那么关于减少realloc调用的原始数量呢?但这些只是被更聪明的字符串增长逻辑所替代的调用。
如果这个格式化字符串构建循环的性能很重要,请进行性能分析。制作一个减少realloc操作的版本,并对其进行性能分析。在你编写的所有目标平台上进行比较,以及在性能很重要的平台上进行比较。
如果性能不是关键问题,请优化好结构、可维护性和代码重用等属性。一个构建格式化字符串的函数不需要知道字符串内存管理的情况。它需要一个抽象接口来处理动态字符串。该接口可以提供一个很好的函数,通过在尾部添加另一个字符串的副本来原地更改字符串。除了格式化字符串生成器之外的其他函数也可以使用这些字符串操作。
字符串管理代码可以在一个地方决定是否每次字符串长度变化都调用realloc,或者是否将存储大小与长度分别跟踪,并因此减少realloc调用的次数。

3

像这样的代码:

str = realloc(str, sizeof(*str) +1);

如果realloc失败,它会返回NULL,但不会free(str)。换句话说——内存泄漏。您需要将realloc的结果分配给另一个指针,然后检查NULL并采取相应措施。

使用多个realloc是好还是坏的做法取决于你想要什么,即性能、可维护性、清晰度等。最好的建议是:按照你喜欢的方式编写代码。然后进行性能剖析。有性能问题吗?没有——那就高兴地用它。有——那就重新编写代码,专注于性能。


3
只有在代码尝试从失败的分配中恢复时,str = realloc(str, sizeof(*str) +1) 才是不好的。如果对于一个分配失败的响应是 exit() 进程,则内存泄漏无关紧要。 - Andrew Henle
除了Andrew的评论之外,如果代码因为空指针而崩溃,很可能是这种情况,那么泄漏也是无关紧要的。 - Kaz
@Kaz - 这并不是保证的。在大多数现代系统上,一切都会很好,但作为一段通用的 C 代码,它是有问题的。请参见:https://dev59.com/cnE95IYBdhLWcg3wp_un - Support Ukraine
@4386427(同样不能保证在非现代系统上,解引用空指针将会崩溃)。 - Kaz
@Kaz 在某些操作系统上,由于malloc()耗尽内存而导致的null指针解引用崩溃可能会导致生成一个相当大的核心文件。这可能会产生不利后果。 - Andrew Henle
@AndrewHenle 现在通常默认禁用,或者通过ulimit严格限制。 - Kaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接