在字符串缓冲区对象中手动分配

发布于 2024-12-03 00:19:10 字数 789 浏览 2 评论 0原文

对于一个小型的嵌入式应用程序,我编写了一些用作字符串缓冲区的函数+结构(类似于 C++ 中的 std::stringstream)。

虽然这样的代码工作正常,但有一些不那么小的问题:

  • 我以前从未用 C 编写过手动分配使用不断增长的内存的函数,因此恐怕仍然存在一些问题尚需解决的怪癖
  • 似乎代码分配的内存远多于实际需要的内存,这是 非常糟糕
  • 由于 valgrind 报告的警告,我已从 malloccalloc 在代码中的一处,成功删除了警告,但我不完全确定我是否真正正确使用它

我的意思是它分配的内容超出了实际需要的示例(使用 56k 文件):

==23668== HEAP SUMMARY:
==23668==     in use at exit: 0 bytes in 0 blocks
==23668==   total heap usage: 49,998 allocs, 49,998 frees, 1,249,875,362 bytes allocated

...它看起来不太正确...

有问题的代码在这里(太大,无法将其复制到 SO 上的 字段中):<一个href="http://codepad.org/LQzphUzd" rel="nofollow">http://codepad.org/LQzphUzd

需要帮助,我很感激您的任何建议!

For a small to-be-embedded application, I wrote a few functions + struct that work as String Buffer (similar to std::stringstream in C++).

While the code as such works fine, There are a few not-so-minor problems:

  • I never before wrote functions in C that manually allocate and use growing memory, thus I'm afraid there are still some quirks that yet need to be adressed
  • It seems the code allocates far more memory than it actually needs, which is VERY BAD
  • Due to warnings reported by valgrind I have switched from malloc to calloc in one place in the code, which sucessfully removed the warning, but I'm not entirely sure if i'm actually using it correctly

Example of what I mean that it allocates more than it really needs (using a 56k file):

==23668== HEAP SUMMARY:
==23668==     in use at exit: 0 bytes in 0 blocks
==23668==   total heap usage: 49,998 allocs, 49,998 frees, 1,249,875,362 bytes allocated

... It just doesn't look right ...

The code in question is here (too large to copy it in a <code> field on SO): http://codepad.org/LQzphUzd

Help is needed, and I'm grateful for any advice!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

樱桃奶球 2024-12-10 00:19:10

您增加缓冲区的方式相当低效。对于每一小段字符串,您都需要 realloc() 内存,这可能意味着分配新内存并复制“旧”内存的内容。这很慢并且会破坏你的堆。

更好的办法是以固定量或固定百分比增长,即使新尺寸为旧尺寸的 1.5 或 2 倍。这也会浪费一些内存,但会使堆更可用,并且不会创建太多副本。

这意味着您必须跟踪两个值:容量(分配的字节数)和长度(字符串的实际长度)。但这应该不会太难。

我将介绍一个函数“FstrBuf_Grow”来处理所有这些事情。您只需使用要添加的内存量来调用它,FstrBuf_Grow 将通过在必要时重新分配且至少根据需要重新分配来确保容量符合要求。

...

void FstrBuf_Grow(FstringBuf *buf, size_t more)
{
    while (buf->length + more) > buf->capacity
        buf->capacity = 3 * buf->capacity / 2;
    buf->data = realloc(buf->data, buf->capacity + 1);
}            

这会将容量乘以1.5,直到数据足够大。您可以根据需要选择不同的策略。

The way you are growing your buffer is rather inefficient. For each little piece of string, you realloc() memory, which can mean new memory is allocated and the contents of the "old" memory are copied over. That is slow and fragments your heap.

Better is to grow in fixed amounts, or in fixed percentages, i.e. make the new size 1.5 or 2 times the size of the old size. That also wastes some memory, but will keep the heap more usable and not so many copies are made.

This means you'll have to keep track of two values: capacity (number of bytes allocated) and length (actual length of the string). But that should not be too hard.

I would introduce a function "FstrBuf_Grow" which takes care of all of that. You just call it with the amount of memory you want to add, and FstrBuf_Grow will take care that the capacity matches the requirements by reallocing when necessary and at least as much as necessary.

...

void FstrBuf_Grow(FstringBuf *buf, size_t more)
{
    while (buf->length + more) > buf->capacity
        buf->capacity = 3 * buf->capacity / 2;
    buf->data = realloc(buf->data, buf->capacity + 1);
}            

That multiplies capacity by 1.5 until data is large enough. You can choose different strategies, depending on your needs.

迷雾森÷林ヴ 2024-12-10 00:19:10

strncat(ptr->data, str, len);,移到ptr->length = ((ptr->length) + len);之前并使用 strncpy(ptr->data+ptr->length.... 并且 Destroy 中的 ptr = NULL; 是 的。

没用 “库”的代码似乎是正确的,但请注意,您正在不断地重新分配缓冲区。通常,您应该很少尝试增加缓冲区(例如,每次需要增加缓冲区时,您都使用 max(2* 当前值)。 size, 4) 作为新的大小),因为增加缓冲区的时间复杂度为 O(n)。在更大的缓冲区中,所以堆增长。

The strncat(ptr->data, str, len);, move before the ptr->length = ((ptr->length) + len); and use strncpy(ptr->data+ptr->length.... And the ptr = NULL; in the Destroy is useless.

The code of the "library" seems to be correct BUT be aware that you are continously reallocating the buffer. Normally you should try to grow the buffer only rarely (for example every time you need to grow the buffer you use max(2* the current size, 4) as the new size) because growing the buffer is O(n). The big memory allocation is probably because the first time you allocate a small buffer. Then you realloc it in a bigger buffer. Then you need to realloc it in a buffer even bigger and so the heap grows.

二智少女 2024-12-10 00:19:10

看起来您正在为每个追加重新分配缓冲区。难道只有当你想要添加的内容超出了它的容纳范围时才应该增长它吗?

重新分配时,您希望使用一种策略来增加缓冲区的大小,该策略可以在分配数量和分配的内存量之间提供最佳权衡。每次达到限制时将缓冲区大小加倍对于嵌入式程序来说可能并不理想。

It looks like you're re-allocating the buffer on every append. Shouldn't you grow it only when you want to append more than it can hold?

When reallocating you want to increase the size of the buffer using a strategy that gives you the best trade off between the number of allocations and the amount of memory allocated. Just doubling the size of the buffer every time you hit the limit might not be ideal for an embedded program.

稚然 2024-12-10 00:19:10

一般来说,对于嵌入式应用程序,最好分配最大消息大小 1-3 倍的循环 FIFO 缓冲区。

Generally for embedded applications it is much better to allocate a circular FIFO buffer 1-3 times the maximum message size.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文