11

当您使用指针在堆上分配动态内存时,

char *buffer_heap = new char[15];

它将在内存中表示为:

 ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þþþ

为什么末尾没有 NULL 终止字符而不是 ýýýý««««««««þþþ?

4

7 回答 7

22

Í 是字节 0xCD,Windows 调试分配器将其写入您的 15 字节内存,以表明它是未初始化的堆内存。未初始化的堆栈将为 0xCC。这个想法是,如果你曾经读取内存并意外地得到这个值,你可以对自己说,“嗯,我可能忘记初始化这个”。此外,如果您将其作为指针读取并取消引用,那么 Windows 将使您的进程崩溃,而如果未初始化的缓冲区填充了随机或任意值,那么有时您会侥幸获得一个有效的指针,而您的代码可能会导致所有各种麻烦。C++ 没有说明未初始化的内存持有什么值,非调试分配器不会浪费时间为每次分配使用特殊值填充内存,因此您绝不能依赖该值存在。

后面是 4 个字节的 ý(字节 0xFD),Windows 调试分配器使用它来指示缓冲区末尾的越界区域。这个想法是,如果你发现自己在调试器中写入一个看起来像这样的区域,你可以想“嗯,我可能已经超出了我的缓冲区”。此外,如果释放缓冲区时值已更改,内存分配器会警告您代码错误。

« 是字节 0xAB,而 þ 是 0xFE。大概这些也是为了吸引眼球(它们不是合理的指针或偏移量,因此它们不构成堆结构的一部分)。我不知道它们意味着什么,可能还有更多像 0xFD 这样的保护数据。

最后,我猜你找到了一个 0 字节,即 15 字节缓冲区末尾之外的第 16 个字节(即从它开始算起的第 31 个字节)。

在不提及您在 Windows 上的情况下以“C++”的形式提出问题表明这就是 C++ 的行为方式。不是,它是 C++ 的一种实现的行为方式,具有特定的编译器选项和/或链接的 dll。C++ 不允许您读取缓冲区的末尾,Microsoft 只是对您很好,让您侥幸逃脱,不会崩溃或更糟。

于 2010-02-19T23:44:02.773 回答
6

你还没有初始化那个内存。你只是看到已经存在的东西......

于 2010-02-19T22:58:30.413 回答
4

你需要初始化它。可以通过显式调用默认构造函数将内置类型初始化为零:

char *b = new char[15]();
于 2010-02-19T23:03:12.107 回答
1

虽然每个 C 样式字符串都表示为一个字符序列,但并非每个字符序列都是一个字符串。

\0 通常在您直接分配字符串文字或您自己添加时出现。只有当您将该数组视为具有将 \0 考虑在内的函数的字符串时,它才有意义。

如果你只是分配内存而不初始化它,它就会充满随机的东西。那里可能有一个 0 也可能没有 - 你将不得不在后续步骤中放一些有意义的东西。是否将其设为字符串取决于您。

于 2010-02-19T22:59:33.907 回答
1

因为char是原生类型,所以它是未初始化的。这就是 C++ 的样子(它是 C 的遗产)。

只需接受并 0 自己终止它:

char *buffer_heap = new char[15];
*buffer_heap = '\0';

或者如果你想初始化整个缓冲区:

std::fill(buffer, buffer + 15, 0);
于 2010-02-19T23:00:46.323 回答
1

仅当您分配已初始化的类型时,它才会被初始化。否则,如果你想要一些有意义的值,你必须自己写。

另一方面,更好的答案是你一开始就不应该这样做。忘记它的new[]存在,不要回头。

于 2010-02-19T23:13:37.390 回答
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;
}
于 2010-02-20T00:16:39.293 回答