【问题标题】:Deleting dynamically allocated variables setting pointer to 0 [duplicate]删除动态分配的变量将指针设置为0 [重复]
【发布时间】:2018-02-05 10:54:17
【问题描述】:

这段代码的结尾我看不懂(array = 0;):

#include <iostream>

int main()
{
    std::cout << "Enter a positive integer: ";
    int length;
    std::cin >> length;

    int *array = new int[length];

    std::cout << "I just allocated an array of integers of length " << length << '\n';

    array[0] = 5; // set element 0 to value 5

    delete[] array; // use array delete to deallocate array
    array = 0; // use nullptr instead of 0 in C++11

    return 0;
}

最后,一个动态分配的数组被删除(返回给OS),然后赋值为0。

为什么要这样做?数组返回给操作系统后,就不用赋值0了吧?

代码来自:http://www.learncpp.com/cpp-tutorial/6-9a-dynamically-allocating-arrays/

【问题讨论】:

  • 它设置了一个有效的(但为空的)指向数组的指针,以避免出现悬空指针。所以以后,我们可以在使用之前测试array。如果指针不再使用,它​​确实是不需要的。
  • int *array,作为一个指针,仍然保存它指向的地址信息,即使对象被释放。由于该空间不再属于该指针,请将其设置为 0,这样您就无法访问一些随机数据。
  • 使用std::vector 可以避免那些手动内存管理。
  • 变量只在编译时才知道。您正在删除
  • 在这种特定情况下,这是多余的,但有些人喜欢在delete之后这样做

标签: c++ arrays pointers memory-management dynamic-allocation


【解决方案1】:

这样做是为了将指针设置为NULL(无论在C++ 中,我们更喜欢nullptr,因为NULL 和0 可能是不同的东西)。

这种策略消除了悬空指针的可能性,因为数组可能已被删除,但这并不意味着它被设置为NULL

如果我们不这样做,我们就会冒着检查指针是否为NULL 的风险(在我们的代码后面),我们会看到它不是NULL,错误地认为指针是正确的被访问,并导致未定义的行为。

【讨论】:

  • 应该设置为nullptr。在 C++ nullptr 中,NULL 和 0 是不同的东西
  • @ArtemyVysotsky 好点,答案已更新。
【解决方案2】:

数组返回给操作系统后,就不用赋值0了吧?

您说得对,因为内存已被operator delete 释放(解除分配),所以不需要它。但是想想这样一种情况,在你使用 delete[] 之后,你可能会在代码的另一个地方(函数、循环等)使用指针。

delete[] 语句被调用(dangling pointer) 后,array 变量仍然保存旧分配的地址。如果您访问该地址,您将获得undefined bahaviour (UB),因为内存不再属于您,在大多数情况下您的程序会崩溃。

为避免您进行null pointer 检查,如下所示:

if (array != nullptr)
{
   /* access array */
   ...
}

根据地址 0 检查指针,地址 0 表示无效地址。

为了使检查成为可能,如果 C++11 不可用,请将指针设置为 nullptrNULLnullptr 关键字引入了类型安全性,因为它的行为类似于指针类型,并且应该优先于类似 C 的 NULL。在 C++11 之前,NULL 被定义为整数 0,因为 C++11 它是 nullptr 的别名。
要定义您自己的 nullptr 以将其用于 C++11 之前的编译器,请查看此处:How to define our own nullptr in c++98?


关于deletedelete[] 的一个有趣事实是在nullptr 上使用它是安全的。它写在cppreference.comSO answer 的第2 点。

运算符删除,运算符删除[]

2) [...] 除非ptr 是空指针或者是先前从operator new[](size_t) 的标准库实现或运算符new[](size_t, std::nothrow_t) 获得的指针,否则此函数的标准库实现的行为是未定义的。

【讨论】:

  • 是的,如果你的设计要求重用指针,那么你需要一个标志来告诉你指针没有指向有效的东西;这就是空指针的用途。它应该在删除某些内容后自动使用。特别是,编写一个析构函数来删除分配的块然后将指针设置为空是很愚蠢的,但这在初学者的代码中很常见。
  • @PeteBecker 为什么会很傻?删除后总是将指针设为空不是一种好习惯,以防以后引入重用?有什么缺点?
  • @Sossisos -- 的原因是析构函数运行后对象不存在并且没有指针 重复使用。不利的一面是,它使编写该代码的人看起来像他们按口号编写代码,而不是思考。
  • @PeteBecker 啊,我很抱歉,我看错了,我认为“愚蠢”是理所当然地涉及空指针,而不是专门在析构函数中这样做。无论如何,我在这里阅读了一些关于为什么不应该理所当然地进行归零的链接文章,并且现在理解了推理(即使我不太同意)。谢谢你的时间:)
【解决方案3】:

您分配给通常称为“无效地址”的值,即NULL0 或指针类型nullptr,否则您无法知道您的指针是否指向无效地址。换句话说,当你 delete[] 你的数组时,你的指针“不知道”它指向一个不再可用的内存地址。

【讨论】:

    【解决方案4】:

    我们正在设置指向NULL (0) 的指针以避免悬空指针(指针仍然指向不再属于您的相同内存)。在局部变量的情况下,如果函数在删除后没有继续,那么它就没有多大用处(因此它的明显指针不会被重用)。在全局/成员指针的情况下,避免错误的良好做法。

    访问已删除的指针可能会导致覆盖/读取随机内存(这可能比崩溃更危险)并导致undefined behavior,而访问NULL 指针将立即崩溃。

    由于,您应该使用nullptr,因为它定义为指针类型,而NULL 更多的是int 类型并提高了类型安全性+ 解决了模棱两可的情况。

    在双重删除指针的情况下,在nullptr上使用delete是安全的,没有任何反应,但是如果你删除已经删除的非空指针,它将导致undefined behavior并且很可能程序会崩溃。

    中,您应该避免使用纯指针,因为存在用于此用途的 STL 容器(它们自己释放资源 (RAII))或 smart pointers

    std::vector<int> array{1,2,3,4,5};
    

    【讨论】:

    • 回复:“避免错误的良好做法”——避免错误的最佳做法是正确设计代码并实施设计。自动将指针设置为 null 以防万一有人粗心 不会避免错误; 设计实现一个使用null来表示指针指向空的系统。
    猜你喜欢
    • 2021-07-28
    • 1970-01-01
    • 2017-12-21
    • 1970-01-01
    • 1970-01-01
    • 2015-05-09
    • 2012-11-06
    • 2021-06-23
    • 2016-03-02
    相关资源
    最近更新 更多