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