【问题标题】:c++: local array definition versus a malloc callc++:本地数组定义与 malloc 调用
【发布时间】:2011-03-17 05:25:06
【问题描述】:

这有什么区别:

somefunction() {  
    ...  
    char *output;   
    output = (char *) malloc((len * 2) + 1);  
    ...  
}  

还有这个:

somefunction() {  
    ...  
    char output[(len * 2) + 1];  
    ...  
}  

什么时候比另一个更合适?

感谢大家的回答。这是一个总结:

  1. 例如。 1是堆分配
  2. 例如。 2是栈分配
  3. 堆栈有大小限制,将其用于较小的分配
  4. 你必须释放堆分配,否则会泄漏
  5. 一旦函数退出就无法访问堆栈分配
  6. 在您释放它(或应用程序结束)之前,可以访问堆分配
  7. VLA 不是标准 C++ 的一部分

欢迎指正。

这里是堆与栈之间区别的一些解释:
What and where are the stack and heap?

【问题讨论】:

  • 您应该查阅您的 C++ 书籍。如果您没有 C++ 书籍,我建议您获取 The Complete C++ Book Guide and List 中列出的初学者文本之一。
  • 另外,这个问题完全取决于len 是如何定义的;如果它不是一个常量,那么第二个代码示例是格式错误的 C++。
  • len 是一个 int 并赋值给函数内部 strlen 的结果。
  • 我不理解人们对“买书”的反应。不是每一个问题都由某本书回答吗?除非您的意思是这个问题由大量书籍回答,在这种情况下,“基本”问题在伟大的 stackoverflow 中是不可接受的吗?我不是指像 James McNellis 这样的特定书籍的指针,只是像 Brian R. Bondy 这样的一般书籍的指针。如何过滤掉标记为基本问题的搜索?还是)感谢你的建议。你怎么拼写C?
  • @Gush:我建议参考一本好书是一个严肃的建议。仅仅通过在互联网上提问是无法学习一门语言的,尤其是像 C++ 这样复杂的语言。任何关于 C 或 C++ 的介绍性文本都会很早就涵盖此类主题,因为对象存储持续时间是该语言的基本部分。微不足道的基本问题虽然在 SO 上是允许的,但令人不安,因为它们通常表明提问者可能正在从一个糟糕的来源学习语言(我并不是说你一定是这种情况,只是经常是这种情况)。

标签: c++ c arrays malloc


【解决方案1】:

第一个在堆上分配内存。你必须记住释放内存,否则它会泄漏。如果内存需要在函数之外使用,或者需要分配大量内存,则这是合适的。

第二个在堆栈上分配内存。函数返回时会自动回收。如果您不需要将内存返回给调用者,这是最方便的。

【讨论】:

  • 另一方面,你不想在堆栈上放太多东西,因为它没有堆那么大。所以,int data[10] 很好,但我根本不会指望int data[10000] 工作。
【解决方案2】:

当你只有少量数据时使用局部变量,并且你不会在你声明它的函数范围之外使用数据。如果你要传递数据,使用 malloc .

局部变量保存在堆栈上,它的大小比堆上的限制要大得多,堆是用 malloc 分配的数组。我通常会将大于 16 字节的任何东西放在堆上,但你有比这更多的灵活性。只是不要在 kb/mb 大小范围内分配局部变量 - 它们属于堆。

【讨论】:

  • +1 用于提及堆栈的大小限制,但我认为 16 字节有点低。大多数平台上的默认堆栈大小不是以兆字节为单位吗(因此在大多数情况下,多 KB 变量应该没问题)?
  • 是的,但是当你在堆栈上分配时,你还应该考虑数据的生命周期以及它的大小。我的意思是 16 个字节用于可能不知道其生命周期的值。您可能会在堆栈上分配几个 kb,然后分支到另一个函数,该函数也分配很多 kb,依此类推 - 它可以很快填满。 (在 StackedCrooked 的回复中对此进行了更多讨论。)
  • 由于问题是关于C++的,我强烈反对使用malloc的推荐。在 C 中,使用 malloc。在 C++ 中,使用 new。
【解决方案3】:

第一个示例从堆中分配一个存储块。第二个从堆栈分配存储。当您从 somefunction() 返回输出时,差异变得明显。动态分配的存储仍然可供您使用,但第二个示例中的基于堆栈的存储,嗯,无处可去。您仍然可以写入此存储并读取一段时间,直到您下次调用函数时,存储将被返回地址、参数等随机覆盖。

这个问题中的代码还有很多其他奇怪的东西。首先,这是一个 c++ 程序,你会想使用 new 而不是 malloc() 所以你会说

output = new char[len+1];

len*2 + 1 到底是怎么回事?也许这在您的代码中有些特殊,但我猜您想分配 unicode 字符或多字节字符。如果它是 unicode,则 null 终止与每个字符一样占用两个字节,并且 char 是错误的类型,在大多数编译器中是 8 位字节。如果它是多字节的,那么,嘿,所有的赌注都没有了。

【讨论】:

  • 我不知道我所做的是否正确 - 但它源于 PQescapeStringConn 的文档 - “'to' 应指向一个能够容纳至少一个字节多于两倍的缓冲区长度值”来自这里:postgresql.org/docs/7.3/static/libpq-exec.html
【解决方案4】:

首先是一些术语:

  • 第一个样本称为堆分配
  • 第二个样本称为堆栈分配

一般规则是:在栈上分配,除非:

  1. 编译时数组所需的大小未知。
  2. 所需大小超过总堆栈大小的 10%。 Windows 和 Linux 上的默认堆栈大小通常为 1 或 2 MB。因此,您的本地数组不应超过 100,000 字节。

【讨论】:

  • 我认为这些一般规则有点弱。例如,我可能会在 main() 中分配 10% 的堆栈,然后调用其他函数(依次调用越来越多的函数,每个函数都会填满堆栈)。在完成所有更深层次的功能之前(在这种情况下基本上是程序结束),我将无法释放该空间。我的程序自动减少了 10% 的堆栈空间。我认为 10% 的规则应该仅限于 函数。
  • @Mark H:你说得对,10% 的规则不应该在 main 中分配,当然也不应该在可重入代码中。但是,我看不出纯函数比非纯函数更安全地防止堆栈溢出。
  • 纯函数不会调用其他函数(除非它们本身是纯函数)——但它们通常会执行一些算法并立即返回。您在其中分配的任何局部变量都将是短暂的,堆栈应该是这样的。具有较长生命周期的变量(如前面的示例)应该放在堆上。
  • 只要纯函数不是递归就是:)
【解决方案5】:

您使用 C++ 和 C 标记了您的问题,但 C++ 中不允许使用第二种解决方案。可变长度数组只允许在 C(99) 中使用。

如果您假设 'len' 是一个常数,那么两者都可以工作。

malloc()(和 C++ 的 'new')在堆上分配内存,这意味着你必须释放()(或者如果你用 'new'、'delete' 分配)缓冲区,否则内存永远不会被回收(泄漏)。

后者在栈上分配数组,超出范围就会消失。这意味着您不能将指针返回到分配范围之外的缓冲区。

当您想要传递内存块时,前者很有用(但在 C++ 中,最好使用 RAII 类进行管理,而不是手动管理),而后者最适合只需要存在于一个范围内。

最后,您可以用“静态”标记其他堆栈分配的数组,以将其从堆栈中取出并放入全局数据部分:

static char output[(len * 2) + 1];

这使您能够返回指向其范围之外的缓冲区的指针,但是,对此类函数的所有调用都将引用同一块全局数据,因此如果您需要一个唯一的内存块,请不要使用它时间。

最后,不要在 C++ 中使用 malloc,除非你有充分的理由(即 realloc)。改用“new”,并使用随附的“delete”。

【讨论】:

  • 来自gcc.gnu.org/onlinedocs/gcc/Variable-Length.html ISO C99 允许可变长度自动数组,作为扩展,GCC 在 C90 模式和 C++ 中接受它们。我想这就是为什么它使用 gcc 为我编译和工作的原因。我可以相信它不是标准的 C++,也不是您所指的语言的正确定义。我应该删除 C++ 标签吗?
  • 我只是想知道您使用的是哪个,但假设我假设 C++,我想我会警告您 VLA 不是标准 C++ 的一部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-18
  • 2016-01-22
  • 1970-01-01
  • 2014-02-16
  • 1970-01-01
  • 2011-01-11
  • 1970-01-01
相关资源
最近更新 更多