6

我正在查看我在网上找到的以下链表代码:

void DeleteAfter(Node **head){
      if(*head==NULL){
            return;
      }else{
            Node *temp = NULL;
            temp = (*head)->next;
            (*head)->next = (*head)->next->next;
            delete temp;
            temp=NULL;
      }
}

我对 C++ 不是很熟练,所以这可能是一个不好的问题,但是为什么 temp 被删除后被设置为 NULL 呢?这是必要的步骤吗?

4

6 回答 6

8

这是不必要的。有些人养成了这样做的习惯,即使没有结果。积极的编译器优化器将消除此代码,因此它实际上不会造成任何伤害。但我会写:

void DeleteAfter(Node *head) {
  if (head) {
    Node *next = head->next;
    if (next) {
      head->next = next->next;
      delete next;
    }
  }
}

注意我消除了一个无用的间接级别并添加了一个检查以确保有一个“节点之后”要删除。

该习惯的基本原理是,如果指针始终引用有效对象或为空,则可以依赖空检查等同于有效性检查。

出于这个原因,Ada(一种经常用于安全关键系统的语言)初始化指向 null 的指针并定义其delete等效运算符以自动将其参数设置为 null。您的 C++ 正在模拟这种行为。

在实践中,这门学科的价值并不是你所希望的。在很长一段时间内,它可以防止一个愚蠢的错误。然而,一件好事是指针内容的调试器显示是有意义的。

于 2013-01-19T17:07:50.750 回答
2

如果该变量temp以后可能会在代码中再次使用,那么最好将其设置为 NULL。

释放指针后通常将指针设置为 NULL 有两个原因。

1.) 一旦你释放了一个指针,指向的地址的内存就不再对你的程序可用。从理论上讲,该内存现在可以被任何其他程序使用,包括操作系统本身!试图释放一个已经被释放的指针,从而指向谁知道什么会导致一个巨大的问题。幸运的是,现代操作系统可以防止这种情况发生,但程序仍然会因非法访问错误而崩溃。释放一个空指针 OTOH 绝对不会做任何事情。

2.) 在使用运算符取消引用它之前,您应该始终检查指针是否为 NULL *。取消引用 NULL 指针将导致运行时错误。取消引用指向任意内存的已释放指针甚至更糟。释放的指针应始终设置为,NULL以便后面的代码可以假定非空指针指向有效数据。否则无法知道指针是否仍然有效。

至于原始问题,指针变量temp在一个不再使用的短函数中声明为局部变量。在这种情况下,没有必要将其设置为 NULL,因为一旦函数返回,它就会超出范围。

然而,线...

(*head)->next = (*head)->next->next;

在尝试取消引用之前无法确保(*head)->next它不为空,这是不行的。

更好的版本应该是...

int DeleteAfter(Node **head){
  Node *node_after = NULL;

  if(*head==NULL)
    return -1;

  node_after = (*head)->next;

  if(node_after == NULL)
    return -1;

  (*head)->next = node_after->next;
  delete node_after;

  return 0;
  }

现在使用该函数的人可以通过返回值检查节点删除是否成功,并且没有尝试删除不存在的节点的风险。

于 2013-01-19T22:21:25.973 回答
1

如果 temp 是全局变量或成员变量,那么设置为 NULL 并不是一个坏主意。

在使用带有 C 代码的保守垃圾收集器后,我养成了将指针设置为 NULL 的习惯。没有指向未使用内存的指针是它找到要收集的垃圾的方式。但在这种情况下,你也应该这样做

temp->next = NULL;
于 2013-01-19T17:48:45.493 回答
1

删除本地指针变量后不必将其设置为NULL。如果要重用指针,应将指针设置为NULL,在检查NULL后,您可以安全地为其分配一个新地址。通常我们为指针成员这样做变量和全局指针变量。

于 2013-01-19T17:14:05.583 回答
0

在您的代码示例中,没有明显的直接好处,但是可以说是长期维护成本收益。这个想法是,在您删除 temp 后,有人最终可能会添加代码来尝试取消对 temp 的引用。这可能只是因为没有注意到删除,或者在删除后移动访问 temp 的较早行。

这是一个例子:

int * i = new int(12);
std::cout << *i << std::endl; // output is 12.
delete i;
// i = 0; // This would cause the program to fail on the next line.
std::cout << *i << std::endl; // output is random for me.

请注意,这不会隐藏缺陷,实际上不将指针设置为 null 会在这种情况下隐藏缺陷,因为 *i 返回一个随机值。

大多数人会说 i = 0 可能由编译器优化,无论哪种方式,对指针的赋值都是无害的。对我来说,在专业发展时,我总是谨慎行事。

于 2015-08-13T16:39:05.483 回答
-1

这没有必要,有些人(包括我)认为这是不好的做法。

将其设置为的动机NULL是您可以事后检查它是否已被删除,如果没有则访问它。此外,这将防止双重删除,因为deleteNULL 指针是无操作的。

另一方面,它可以隐藏错误。如果对象被删除了,那么使用它就没有意义了,对吧?您应该知道该对象已被删除,而不是依靠检查。

例如

if (p != NULL) //or just if (p)
   p->doStuff()

为什么?你不知道它是否被删除了吗?清理不是逻辑的一部分吗?

于 2013-01-19T17:07:25.680 回答