当你使用指针在堆上分配动态内存时,
char *buffer_heap = new char[15];
它将在内存中表示为:
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þþþ
为什么末尾没有空字符,而是以ýýýý««««««««þþþ作为结尾?
当你使用指针在堆上分配动态内存时,
char *buffer_heap = new char[15];
它将在内存中表示为:
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þþþ
为什么末尾没有空字符,而是以ýýýý««««««««þþþ作为结尾?
在Windows中,Í代表字节0xCD,Windows调试分配器会将其写入你的15个字节内存中以表示未初始化的堆内存。未初始化的栈则是0xCC。这样做的想法是,如果你读取内存并意外地得到这个值,你可以想一想,“嗯,我可能忘记初始化了”。此外,如果你将其作为指针并进行取消引用操作,Windows会崩溃你的进程,而如果未初始化的缓冲区被填充了随机或任意值,那么有时你可能会得到一个有效的指针,你的代码可能会造成各种麻烦。C++不会说明未初始化内存保存什么值,非调试分配器不会浪费时间为每个分配填充内存以特殊的值,因此你绝不能依赖于那个值。
接下来是4字节的ý(字节0xFD),Windows调试分配器用它来表示缓冲区末尾越界的区域。这样做的想法是,如果你在调试器中发现自己正在写入一个看起来像这样的区域,你可以想到“嗯,我可能越界了我的缓冲区”。此外,如果当缓冲区被释放时该值已更改,内存分配器可以警告你的代码有误。
«代表字节0xAB,þ则是0xFE。这些也可能被用作眼镜蛇(它们不是合理的指针或偏移量,因此它们不构成堆结构的一部分)。我不知道它们的意义,可能是像0xFD一样的更多保护数据。
最后,我猜你找到了一个0字节,它是你15个字节缓冲区之外的第16个字节(即从它的开头数第31个字节)。
如果没有提到你在Windows上工作,仅仅将问题描述为“C++”,这可能会让人误以为这就是C++的行为。实际上这不是C++的行为,而是特定编译器选项和/或链接的动态链接库导致的行为。C++不允许你读取超出缓冲区末尾的内容,Microsoft只是对你好,让你没有崩溃或更严重的后果。
您没有初始化该内存。您只能看到已经存在的内容...
您需要进行初始化。内置类型可以通过显式调用默认构造函数初始化为零:
char *b = new char[15]();
因为char
是一种本地类型,所以它未初始化。这就是C++的工作方式(这是C的遗留问题)。
接受这一点并自己进行0终止:
char *buffer_heap = new char[15];
*buffer_heap = '\0';
或者如果你想要初始化整个缓冲区:
std::fill(buffer, buffer + 15, 0);
虽然每个C风格的字符串都表示为char序列,但并不是每个char序列都是字符串。
\0通常在直接赋值字符串字面量时出现,或者您自己添加它。只有在使用考虑到\0的函数处理该数组作为字符串时,它才有意义。
如果您只分配内存而不初始化它,则其中充满了随机内容。可能会有一个0,也可能没有-您将不得不在随后的步骤中放置一些有意义的内容。由您决定是否将其设置为字符串。
只有在分配已初始化的类型时才会初始化。否则,如果你想要一些有意义的值,你就必须自己编写它们。
另一方面,更好的答案是你根本不应该这样做。忘记 new[]
的存在,并且不要再回头看它。
new[]
的存在,花一些时间弄清楚如何覆盖放置和数组的新建和删除,然后无论如何都使用向量。 - Steve Jessop#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;
}
std::string
。那么这些数据是什么呢?它只是那里恰好存在的任何内容。大多数编译器实际上会用调试数据或其他信息填充这些数据,因此当你使用未初始化的数据时,它可能具有一致的模式。 - GManNickG