【问题标题】:c++ - string of 64 ASCII characters overflows malloc(64 * sizeof(char))c++ - 64 个 ASCII 字符的字符串溢出 malloc(64 * sizeof(char))
【发布时间】:2016-08-09 21:21:48
【问题描述】:

如果我提供一个长度为 64 个字符的十六进制字符串(即:26C8D8AB82B027808A371BC46EA789364AB8419F2B17EADFE955CBE5C6369011),以下代码将引发错误,即使我为其分配了 64 * sizeof(char) 字节,这应该是够了:

char* username = (char*)malloc(64 * sizeof(char));
std::cin >> username;
free(username);

当我释放分配的内存时,在第三行抛出错误:

CRT 检测到应用程序在堆结束后写入内存 缓冲区。

这不会发生在 63 个字符或更少的情况下。谁能告诉我为什么 64 * sizeof(char) 是不够的,为什么在释放内存时会抛出错误而不是之前...

【问题讨论】:

  • @TimStraubinger std::malloc 当然是 C++ ......但你是对的,它不应该在没有充分理由的情况下使用(例如需要将分配所有权传递给 C 代码)。
  • @TimStraubinger 是一样的,new[] 和 free() 给出相同的结果。但是,删除不会引发错误。不过谢谢你的建议。
  • @TimStraubinger ...什么? newdelete 也不应该被推荐用于一般用途,因为 stdlib 提供了这么多处理动态分配的安全类,因此用户不必这样做。多态性也不需要动态分配,这就是我假设你试图用“oop-style objects”说的(如果我听说过,这是一个重言式);只需要一个指针或引用,并且目标可以在堆栈上。如果您要向新用户推荐东西,请推荐好东西。
  • @underscore_d:我认为 Tim 区分了 POD(普通旧数据),其中 malloc()free() 可以安全使用,与具有非平凡构造的“对象”,其中 @ 987654326@ 给出的结果还不是一个有效的对象。多态性不是其中的一部分。
  • @MikeMouawad:你不知道关于抛出错误的说法有多准确。这主要是 mallocnew 之间 C++ 代码的区别(除了为非普通旧数据类型调用 ctor / dtor)。如果失败会抛出异常。

标签: c++ visual-c++ memory-management console-application


【解决方案1】:

C 字符串以 NULL 结尾。

你没有为终结者留出空间。

释放内存时检测到错误,因为该函数查看了对象后的填充并发现它已损坏。如果您禁用内存调试,则可能不会进行任何检查(甚至可能没有填充),并且此类错误可能不会被检测到,直到它丢弃了完全不相关的数据。

如果您已经知道确切的长度并且不需要终止符来标记结尾,则可以使用

cin.read(username, 64);

这不会存储终止符,也不会读取多于(或少于)64 个字符的输入,因此不会溢出。

【讨论】:

  • std::cin >> username 插入了一个空终止符,但由于原始缓冲区没有分配到足够大以容纳空终止符,因此在读取 64 个输入字符后发生了缓冲区溢出。你可以使用std::setw()来避免缓冲区溢出:char* username = new char[65]; std::cin >> std::setw(65) >> username; ...当然,使用std::string而不是char[]会更安全:std::string username; std::cin >> username;,然后在需要时使用username.c_str()
  • @RemyLebeau:只要您承认返回的 C 字符串仅在原始 std::string 在范围内时有效。这与分配和稍后释放 char 数组的行为非常不同。可以说它不太安全。
  • @AndonM.Coleman 我学会了不要使用 c_str 除非有必要,因为当字符串超出范围时数组将被销毁,就像将它从函数传递给它刚刚得到的调用函数时销毁。
  • @AndonM.Coleman:这取决于编码。 std::string username 可以在分配 char *username 的同时轻松分配,并且在调用 free(username) 的同时保持在范围内。
  • @MikeMouawad:只有在 std::string 被销毁后,您仍持有 c_str() 返回的 char* 指针,这才是正确的。无论如何,你不应该那样做。在您需要char* 时调用c_str(),此时std::string 仍在范围内。
【解决方案2】:

C 风格的字符串必须比您尝试放入其中的字符数多一个 char,以便为终止的空字符留出空间。

【讨论】:

    【解决方案3】:

    您忽略了字符串末尾的 NUL CHAR ('\0')...

    你应该为最大长度为 64 的字符串分配 65 个字节

    【讨论】:

    • ASCII 字符的名称是 NUL,而不是 NULL(这是 C 对空指针的定义)。只是这么说,因为不幸的是,在实际代码中使用 NULL 作为字符串结尾很常见(而且是错误的)。
    • 是的,角色是'\0'。 ...但是当然,如​​果写一个"string literal",它会自动附加,所以包含它"like this\0" 将是没有意义和浪费的。相反,它用于其他情况,例如在循环一个空终止的缓冲区时检查结束。
    • @hyde 感谢细微差别,经过编辑以反映 NUL 和 '\0'。
    • @Mark:但在 C 中,NULL((void*)0),它不会隐式转换为 char,而在 C++ 中,NULL 可能会在某些时候更改为 nullptr .没错,这不会向后兼容,但不会是“破坏性”更改,因为它只会改变已经破坏的代码的行为。
    • @Mark 因为没有隐式向下转换会使int* p = (void*)0; 成为编译错误。
    猜你喜欢
    • 2015-08-09
    • 2021-07-12
    • 2011-03-04
    • 2010-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-11
    相关资源
    最近更新 更多