动态内存分配问题
Posted
技术标签:
【中文标题】动态内存分配问题【英文标题】:Dynamic memory allocation question 【发布时间】:2011-01-19 00:03:02 【问题描述】:当你使用指针在堆上分配动态内存时,
char *buffer_heap = new char[15];
它将在内存中表示为:
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þþþ
为什么末尾没有 NULL 终止字符,而不是 ýýýý««««««««þþþ?
【问题讨论】:
首先,谁说这甚至是一个字符串?对于编译器,您只需要 15 个原始字节的内存。如果你想要一个字符串,使用std::string
。那么这个数据是什么?这只是发生在那里的任何事情。大多数编译器实际上会用调试数据或其他信息填充这些数据,因此当您使用未初始化的数据时,它可能会得到一致的模式。
我不知道为什么人们不赞成这个,这是一个完全有效的问题。仅仅因为 OP 误解了某事并不意味着我们应该为此惩罚他。
相关:***.com/questions/2029651/… ***.com/questions/958549/dynamically-allocated-char 一辈子都找不到完全相同的副本,但我发誓有一个...
@dmckee:这里是另一个相关的:***.com/questions/370195/…。在我回答之前,我可能应该想到谷歌“site:***.com 0xCD 0xFD”,但这仍然不是一个完全正确的骗局。
@GMan:+1,很高兴看到有人支持 n00bs;缺乏知识并不是一种罪过,而不必费心去了解它并为此感到骄傲是:)
【参考方案1】:
你还没有初始化那个内存。你只是看到已经存在的东西......
【讨论】:
【参考方案2】:虽然每个 C 样式字符串都表示为一个字符序列,但并非每个字符序列都是一个字符串。
\0 通常在您直接分配字符串文字或您自己添加时出现。只有当您将该数组视为具有将 \0 考虑在内的函数的字符串时,它才有意义。
如果你只是分配内存而不初始化它,它就会充满随机的东西。那里可能有一个 0 也可能没有 - 你将不得不在后续步骤中放一些有意义的东西。是否将其设为字符串取决于您。
【讨论】:
为什么总有 ýýýý««««««««þþþ 附加到结尾? @Dave17:那里没有总是相同的数据。进行循环以进行 100 个新的 char[15] 分配并查看。如果它是总是相同的,那么它可能是你的编译器使用的调试模式。 我正在使用 VS-2005,我尝试了 1000 个新字符,但还是一样。 @Dave:那么您看到的只是调试数据,或其他内存跟踪信息,这些信息在释放时放置在那里。这不是你可以指望的东西,它只是垃圾。【参考方案3】:因为char
是本机类型,所以它是未初始化的。 C++ 就是这样(它是 C 的遗产)。
只需接受,然后 0 自行终止:
char *buffer_heap = new char[15];
*buffer_heap = '\0';
或者如果你想初始化整个缓冲区:
std::fill(buffer, buffer + 15, 0);
【讨论】:
【参考方案4】:你需要初始化它。可以通过显式调用默认构造函数将内置类型初始化为零:
char *b = new char[15]();
【讨论】:
【参考方案5】:只有当你分配一个已初始化的类型时,它才会被初始化。否则,如果你想要一些有意义的值,你必须自己写。
另一方面,更好的答案是您一开始就不应该这样做。忘记new[]
的存在,不要回头。
【讨论】:
仅适用于高级用户:记住new[]
存在,花点时间弄清楚如何覆盖放置和数组new 和delete,然后还是使用向量。
@Steve:或者就像关于优化的老话所说:规则#1:不要这样做。规则 #2(仅适用于高级程序员):现在不要这样做。
第 3 条规则(适用于超级高级程序员):停止修补并交付该死的东西 ;-)【参考方案6】:
Í 是字节 0xCD,Windows 调试分配器将其写入您的 15 字节内存,以表明它是未初始化的堆内存。未初始化的堆栈将为 0xCC。这个想法是,如果你曾经读取内存并意外地得到这个值,你可以对自己说,“嗯,我可能忘记初始化这个了”。此外,如果您将其作为指针读取并取消引用,那么 Windows 将使您的进程崩溃,而如果未初始化的缓冲区填充了随机或任意值,那么有时您会侥幸获得一个有效的指针,而您的代码可能会导致所有各种麻烦。 C++ 没有说明未初始化的内存持有什么值,非调试分配器不会浪费时间为每次分配使用特殊值填充内存,因此您绝不能依赖该值存在。
后面是 4 个字节的 ý(字节 0xFD),Windows 调试分配器使用它来指示缓冲区末尾的越界区域。这个想法是,如果你发现自己在调试器中写入一个看起来像这样的区域,你可以想“嗯,我可能已经超出了我的缓冲区”。此外,如果在释放缓冲区时值发生了变化,内存分配器会警告您代码错误。
« 是字节 0xAB,而 þ 是 0xFE。大概这些也是为了吸引眼球(它们不是合理的指针或偏移量,因此它们不构成堆结构的一部分)。我不知道它们是什么意思,可能是 0xFD 之类的更多保护数据。
最后,我猜你找到了一个 0 字节,即 15 字节缓冲区末尾之外的第 16 个字节(即从它的开头算起的第 31 个字节)。
以“C++”的形式提出问题而没有提及您使用的是 Windows,这表明这就是 C++ 的行为方式。不是,它是 C++ 的一种实现的行为方式,具有特定的编译器选项和/或链接的 dll。 C++ 不允许您读取缓冲区的末尾,Microsoft 只是对您很好,让您侥幸逃脱,不会崩溃或更糟。
【讨论】:
+1 用于详细说明每个十六进制代码,特别是用于说明调试器的技巧;也用于解释标记学科 我添加了 visual-c++ 标签,因为你是对的,问题需要它。 OP 可能不知道这是特定于实现的行为。【参考方案7】:在 Linux 上的 GNU C++ (g++) 中,这个程序很快就退出了:
#include <algorithm>
#include <iterator>
#include <vector>
#include <cstddef>
#include <cstdlib>
#include <iostream>
namespace
class rand_functor
public:
int operator ()() const return ::std::rand();
;
int main()
using ::std::cout;
using ::std::vector;
using ::std::ostream_iterator;
using ::std::generate;
using ::std::equal;
using ::std::copy;
char *tmp = new char[1000];
// This just fills a bunch of memory with random stuff, then deallocates it
// in the hopes of making a match more likely.
generate(tmp, tmp+1000, rand_functor());
delete[] tmp;
vector<char *> smalls;
smalls.push_back(new char[15]);
do
smalls.push_back(new char[15]);
while (equal(smalls[0], smalls[0]+15, smalls[smalls.size() - 1]));
cout << " In one allocation I got: [";
copy(smalls[0], smalls[0]+15, ostream_iterator<char>(cout));
cout << "]\nAnd in another allocation I got: [";
copy(smalls[smalls.size() - 1], smalls[smalls.size() - 1]+15,
ostream_iterator<char>(cout));
cout << "]\n";
cout << "It took " << smalls.size() << " allocations to find a non-matching one.\n";
return 0;
【讨论】:
以上是关于动态内存分配问题的主要内容,如果未能解决你的问题,请参考以下文章