【问题标题】:Why bad_alloc() exception thrown in case of size_t为什么在 size_t 的情况下会抛出 bad_alloc() 异常
【发布时间】:2016-07-11 08:30:46
【问题描述】:

我正在处理下面的代码,当我执行这段代码时,我得到了一个std::bad_alloc 异常:

int _tmain(int argc, _TCHAR* argv[])
{
 FILE * pFile;

 size_t state;
 pFile = fopen("C:\\shared.tmp", "rb");
 if (pFile != NULL)
 {
    size_t rt = fread(&state, sizeof(int), 1, pFile);
    char *string = NULL;
    string= new char[state + 1];
    fclose(pFile);
 }
 return 0;
}

下面这行导致抛出异常:

string = new char[state + 1];

为什么会发生这种情况,我该如何解决?

【问题讨论】:

  • 您可以在分配前打印状态并发布结果吗?
  • 调试器、断点和监视窗口可能会告诉您要分配的东西有多大。我的水晶球告诉我你的尺寸以与size_t 不一致的形式存储,但无论如何你都将它视为一个,特别是因为你正在读取int 的尺寸,并存储在一个`size_t,在你的平台上可能分别是32位和64位。哎哟。
  • 我也会担心字节顺序,因为这段代码直接读入整数类型的内部表示,而不是用字节构造它。 OP 可能认为他正在读取值为 0x000000FF 的整数,但他可能会得到 0xFF0000000!
  • @HumamHelfawi - 分配前请看打印结果 - "3435973638"
  • 如果我在使用前初始化 size_t state =0 对我的代码有影响吗?

标签: c++ windows exception


【解决方案1】:

您正在传递一个未初始化的 64 位(8 字节,在现代 64 位系统上)变量 state 的地址,并告诉 fread 读取 sizeof(int)(32 位,4 字节)相同的系统)字节从文件到这个变量。

这将用读取的值覆盖变量的 4 个字节,但其他 4 个字节未初始化。它覆盖的 4 个字节取决于架构(在 Intel CPU 上最不重要,在大端配置的 ARM 上最重要),但结果很可能是垃圾,因为 4 个字节未初始化并且可能包含任何内容.

在您的情况下,它们很可能是最重要的字节,并且至少包含一个非零位,这意味着您随后会尝试分配远远超过 4GB 的内存,而您没有。

解决方案是将state 设为std::uint32_t(因为您显然希望该文件包含代表无符号整数的4 个字节;不要忘记包含<cstdint>)并传递sizeof(std::uint32_t),并且通常确保对于传入指针和大小的每个fread 和类似调用,确保指针指向的东西实际上具有您传递的大小。在 64 位系统上传递 size_t* 和 sizeof(int) 并不能满足这些要求,并且由于 C++ 基本类型的大小无法保证,因此您通常根本不想将它们用于二进制 I/O。

【讨论】:

  • 我尝试使用 std::uint32_t,但仍然遇到同样的 bad_alloc() 错误。请查看 fread 调用后的状态值 -“3435973238”(这是 size_t)。使用 std::uint32 它比这更高。实际上我的问题是我的“状态”变量未从 fread() 函数初始化。
【解决方案2】:

您可以在 C++ 代码中改进很多方面,但有很多原因导致您最终出现这种行为:

首先,变量state 的类型为size_t,但您的代码尝试使用fread(&state, sizeof(int), 1, pFile); 初始化其值。现在,如果 sizeof(state) != sizeof(int) 那么你有未定义的行为。如果sizeof(state) < sizeof(int),那么fread 语句通常会在存储变量state 之后覆盖一些任意内存。这会导致未定义的行为(例如,state 可能有一些随机的大值,并且分配失败)。

其次,如果sizeof(state) > sizeof(int),那么state 只是部分初始化,它的实际值取决于初始化(由fread)和未初始化的位。所以它的值可能很大,分配可能会失败。

第三,如果sizeof(state) == sizeof(int)那么可能是读取的值太大,内存不足导致分配失败。

第四,您从文件中读取的值可能有一些不同的编码或字节序。例如,如果 value 以 big-endian 格式写入文件,但在 little-endian CPU 上是 fread,则可能会导致字节交换错误。在使用读取的值之前,您可能需要交换字节。

我建议你改用来自<cstdint>(或<stdint.h> 用于pre-C++11)的一些固定宽度整数类型,例如std::uint64_t 用于变量state,使用fread(&state, sizeof(state), 1, pFile); 读取值, 然后字节交换 state 如果您的 CPU 的字节序与文件中存储的值的字节序不匹配。

【讨论】:

    【解决方案3】:

    你应该决定你愿意分配的最大字符数是多少,如果state 大于这个值,就会出错。几乎可以肯定,确实如此。

    【讨论】:

      猜你喜欢
      • 2010-12-29
      • 1970-01-01
      • 2016-10-23
      • 1970-01-01
      • 1970-01-01
      • 2012-04-23
      • 2015-12-17
      • 2012-01-16
      • 1970-01-01
      相关资源
      最近更新 更多