使用指针在堆上分配动态内存时
char *buffer_heap = new char[15];
它将在内存中表示为:
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þþþ
为什么最后没有一个NULL终止字符而不是“««««««««?
答案 0 :(得分:22)
Í是字节0xCD,Windows调试分配器写入您的15字节内存,表明它是未初始化的堆内存。未初始化的堆栈将为0xCC。这个想法是,如果你曾经读过记忆并意外地得到这个价值,你可以自己想一想,“嗯,我可能忘记了这个”。此外,如果您将其作为指针读取并取消引用它,那么Windows将使您的进程崩溃,而如果未初始化的缓冲区填充了随机或任意值,那么有时您会得到一个有效的指针,并且您的代码可能会导致所有各种麻烦。 C ++没有说明未初始化内存的值是什么,非调试分配器不会浪费时间为每个分配填充特殊值的内存,所以你绝不能依赖那个值。
接下来是4个字节的ý(字节0xFD),Windows调试分配器使用它来指示缓冲区末尾的越界区域。我的想法是,如果你发现自己在调试器中写了一个看起来像这样的区域,你可以想“嗯,我可能在这里超出了我的缓冲区”。此外,如果释放缓冲区时值已更改,则内存分配器可以警告您代码错误。
«是字节0xAB,þ是0xFE。据推测,这些也可用作引人注目的(它们不是合理的指针或偏移量,因此它们不构成堆结构的一部分)。我不知道它们的含义,可能还有更多的保护数据,如0xFD。
最后,我猜,你已经找到了一个0字节,超出15字节缓冲区末尾的第16个字节(即从它开始算起的第31个字节)。
在不提及您使用Windows的情况下将问题称为“C ++”表明这就是C ++的行为方式。它不是,它是一个C ++实现的行为,特定的编译器选项和/或链接的dll。 C ++不允许你读取缓冲区的末尾,微软对你很好,让你逃脱它不会崩溃或更糟。
答案 1 :(得分:6)
您尚未初始化该内存。你只是看到那里已有的东西......
答案 2 :(得分:4)
您需要初始化它。通过显式调用默认构造函数,可以将内置类型初始化为零:
char *b = new char[15]();
答案 3 :(得分:1)
虽然每个C样式字符串都表示为字符序列,但并不是每个字符序列都是字符串。
当您直接指定字符串文字或自己添加字符串文字时,\ 0通常会出现。并且只有将该数组视为具有将\ 0考虑在内的函数的字符串才有意义。
如果您只是分配内存并且没有初始化它,那么它就是随机的东西。那里可能有0或者可能没有 - 你将不得不在后续步骤中放置有意义的东西。是否要将某些东西变成字符串取决于你。
答案 4 :(得分:1)
因为char
是本机类型,所以它未初始化。这就是C ++的原因(它是C的遗产)。
接受它并且0自己终止它:
char *buffer_heap = new char[15];
*buffer_heap = '\0';
或者如果您想要初始化整个缓冲区:
std::fill(buffer, buffer + 15, 0);
答案 5 :(得分:1)
只有在分配了已初始化的类型时才会初始化它。否则,如果你想要一些有意义的值,你必须自己写。
另一方面,更好的答案是你不应该首先这样做。忘记new[]
存在,不要回头。
答案 6 :(得分:0)
在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;
}