C++ 动态内存分配问题

C++ 动态内存分配问题,c++,visual-c++,dynamic-memory-allocation,C++,Visual C++,Dynamic Memory Allocation,使用指针在堆上分配动态内存时 char *buffer_heap = new char[15]; 它将在内存中表示为: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þþþ 为什么结尾没有空终止字符,而不是ýýý««««««««««««þþ»?您尚未初始化该内存。您只是看到了已经存在的内容…虽然每个C样式的字符串都表示为一个字符序列,但不是每个字符序列都是一个字符串 \0通常在您直接分配字符串文字或您自己在其中添加字符串文字时出现。只有将该数组视为包含考虑\0的函数的字符串时,它才

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

char *buffer_heap = new char[15];
它将在内存中表示为:

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

为什么结尾没有空终止字符,而不是ýýý««««««««««««þþ»?

您尚未初始化该内存。您只是看到了已经存在的内容…

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

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


如果你只分配内存而不初始化它,它就充满了随机的东西。那里可能有0,也可能没有-在后续步骤中,您必须在那里放置一些有意义的内容。这取决于你是否把它变成一根绳子

因为
char
是本机类型,所以未初始化。这就是C++的方式(它是C的遗产)。 只需接受它,然后自己终止它:

char *buffer_heap = new char[15];
*buffer_heap = '\0';
或者,如果要初始化整个缓冲区:

std::fill(buffer, buffer + 15, 0);

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

char *b = new char[15]();

只有在分配已初始化的类型时,才会初始化它。否则,如果你想在那里得到一些有意义的值,你就必须自己写


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

Í是字节0xCD,Windows调试分配器将其写入15字节的内存中,以指示它是未初始化的堆内存。未初始化的堆栈将是0xCC。这个想法是,如果你读过内存,意外地得到这个值,你可以想,“嗯,我可能忘了初始化这个”。此外,如果将其作为指针读取并取消引用,Windows将使进程崩溃,而如果未初始化的缓冲区中填充了随机或任意值,则有时会侥幸获得有效指针,代码可能会导致各种问题。C++没有说明未初始化的内存所包含的值,而非调试分配器不会浪费时间为每个分配填充特定的值,因此,你永远不能依赖于存在的值。

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

«是字节0xAB,而þ是0xFE。大概它们也是为了吸引眼球(它们不是可信的指针或偏移量,所以它们不构成堆结构的一部分)。我不知道它们意味着什么,可能是更多的保护数据,比如0xFD

最后,我想,您已经找到了一个0字节,第16个字节超出了15字节缓冲区的末尾(即,从开始算起的第31个字节)

当你不把你的问题写在Windows上时,把这个问题当作“C++”来说明这就是C++的行为方式。它不是,它是C++的一种实现方式,它有特定的编译器选项和/或链接的DLL。C++不允许你读过缓冲区的末尾,微软只是对你很好,让你摆脱它,而不是崩溃或更糟。Linux的GNU C++(G++)中的

< P>,这个程序很快退出:

#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;
}
#包括
#包括
#包括
#包括
#包括
#包括
名称空间{
类rand_函子{
公众:
int运算符()()常量{return::std::rand();}
};
}
int main()
{
使用::std::cout;
使用::std::vector;
使用::std::ostream_迭代器;
使用::std::generate;
使用::std::equal;
使用::std::copy;
char*tmp=新字符[1000];
//这只是用随机的东西填充一堆内存,然后释放它
//希望能让比赛更有可能。
生成(tmp,tmp+1000,rand_函子());
删除[]tmp;
小病媒;
smalls.push_back(新字符[15]);
做{
smalls.push_back(新字符[15]);
}而(等于(smalls[0],smalls[0]+15,smalls[smalls.size()-1]);

为什么总是有ýýý««««««««««««««««««««««««««««««««««««««««««««»»»»»»»分配并查看。如果总是相同的,则可能是编译器使用的调试模式。我使用的是VS-2005,我尝试了1000个新字符,但仍然是相同的。@Dave:那么您只是看到了调试数据,或者在释放时放在那里的其他内存跟踪信息。这不是您可以依赖的,而是just垃圾。首先,谁说这甚至是一个字符串?对于编译器来说,您只需要15个原始字节的内存。如果您想要一个字符串,请使用
std::string
。那么这是什么数据呢?它只是那里发生的任何事情。大多数编译器实际上会用调试数据或其他信息填充此数据,所以当您使用未初始化的数据时,它是可能有一个一致的模式。我不知道为什么人们反对这个,这是一个完全正确的问题。仅仅因为OP误解了一些事情并不意味着我们应该为此惩罚他。相关:我一生都找不到一个完全相同的,但我发誓有一个…@dmckee:这里还有一个相关的:。我可能应该有在我回答之前,我想了想谷歌“site:stackoverflow.com 0xCD 0xFD”,但这仍然不是一个完全的骗局。@GMan:+1,我很高兴看到有人支持n00bs;缺乏知识不是一种罪过,而不费心去了解它并为此感到自豪是:)仅适用于高级用户:记住
new[]
存在,请花一段时间图