c ++:动态分配的新运算符是否检查内存安全?

c++: Does the new operator for dynamic allocation check for memory safety?

我的问题来自我的一个 c++ 练习(来自 C++ 中的编程抽象,2012 版,练习 12.2)。这是:

void strcpy(char *dst, char *src) {
    while (*dst++ = *src++);
}

The definition of strcpy is dangerous. The danger stems from the fact that strcpy fails to check that there is sufficient space in the character array that receives the copy, thereby increasing the chance of a buffer-overflow error. It is possible, however, to eliminate much of the danger by using dynamic allocation to create memory space for the copied string. Write a function

char *copyCString(char *str);

that allocates enough memory for the C-style string str and then copies the characters—along with the terminating null character—into the newly allocated memory.

这是我的问题: 这种新方法真的安全吗?为什么它是安全的? 我的意思是,有点激进,如果堆中没有足够的 space 怎么办? new 运算符是否能够检查 space 可用性并在 space 不足时以优雅的方式下降? 这会导致其他类型的 "something-overflow" 吗?

如果 new 未能分配请求的内存,它应该抛出 std::bad_alloc 异常(但请参阅下文了解更多信息)。之后,堆栈将展开到匹配的异常处理程序,您的代码将决定从那里做什么。

如果你真的 want/need 确保不会抛出异常,有一个 nothrow 版本的 new 你可以使用它将 return 一个空指针指向信号失败——但这几乎完全是为了 C 兼容性而包含的,并不经常使用(或有用)。

对于问题中提到的情况类型,您通常希望使用 std::string 而不是自己分配 space。

另请注意,在许多现代系统中,new 在失败时抛出或 return 空指针的概念确实相当外国。实际上,Windows 通常会尝试扩展分页文件以满足您的要求。 Linux 有一个 "OOMKiller" 进程,它将尝试查找 "bad" 进程并在你 运行 退出时杀死它们以释放内存。

因此,即使 C++ 标准(和 C 标准)规定了如果分配失败应该发生什么,现实生活中很少发生这种情况。

如果新运算符无法分配内存,它将抛出 bad_alloc 异常,除非未指定抛出异常。如果您指定常量 nothrow,如果它无法分配内存,您将返回 NULL 指针。

strcpy 的代码是不安全的,因为它会尝试在为 dst 指针分配的内存之外进行复制。示例:

int main()
{
    const char* s1 = "hello"; // allocated space for 6 characters
    char* s2 = new char[ 2 ]; // allocated space for 2 characters.
    strcpy( s2, s1 );
    cout << s2 << endl;

    char c; cin >> c;
    return 0;
}

这会打印出正确的值 "hello",但请记住指针 s2 被分配为只有 space 两个字符。所以我们可以假设其他字符被写入后续内存槽,这是不安全的,因为我们可能会覆盖数据或访问无效内存。

考虑这个解决方案:

char* e4_strdup( const char*& c )
{
    // holds the number of space required for the c-string
    unsigned int sz{ 0 };

    // since c-style strings are terminated by the '[=11=]' character,
    // increase the required space until we've found a '[=11=]' character.
    for ( const char* p_to_c = c; *p_to_c != '[=11=]'; ++p_to_c )
        ++sz;

    // allocate correct amount of space for copy.
    // we do ++sz during allocation because we must provide enough space for the '[=11=]' character.
    char* c_copy{ new char[ ++sz ] }; // extra space for '[=11=]' character.
    for ( unsigned int i{ 0 }; i < sz; ++i )
        c_copy[ i ] = c[ i ]; // copy every character onto allocated memory

    return c_copy;
}

如果 运行 内存不足,new 运算符仍会 return 一个 std::bad_alloc 异常。