我正在查看我在网上找到的以下链接列表代码:
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?这是必要的一步吗?
答案 0 :(得分:7)
没必要。有些人养成这样做的习惯,即使没有结果。积极的编译器优化器将消除此代码,因此它实际上不会造成任何伤害。但我会写:
void DeleteAfter(Node *head) {
if (head) {
Node *next = head->next;
if (next) {
head->next = next->next;
delete next;
}
}
}
注意我消除了无用的间接级别,并添加了一个检查以确保删除后的“节点”。
习惯的基本原理是,如果指针总是引用有效对象或为null,则可以依赖空检查等效于有效性检查。
出于这个原因,Ada是一种常用于安全关键系统的语言,它将指针初始化为null并定义其delete
等效运算符以自动将其参数设置为null。你的C ++正在模拟这种行为。
在实践中,这门学科的价值并不是你所希望的。很长一段时间它可以防止愚蠢的错误。然而,一件好事是调试器显示指针内容是有意义的。
答案 1 :(得分:1)
删除后你不必将本地指针变量设置为NULL。如果你想重用指针,你应该将指针设置为NULL,在NULL检查之后,你可以安全地为它分配一个新地址。通常我们这样做用于指针成员变量和全局指针变量。
答案 2 :(得分:1)
如果temp是全局变量或成员变量,那么设置为NULL并不是一个坏主意。
在使用带有C代码的保守垃圾收集器后,我养成了将指针设置为NULL的习惯。没有指向未使用内存的指针是它如何找到要收集的垃圾。但在这种情况下你也应该
temp->next = NULL;
答案 3 :(得分:1)
如果稍后可能在代码中再次使用变量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;
}
现在使用该功能的人可以通过返回值检查节点删除是否成功,并且没有尝试删除不存在的节点的风险。
答案 4 :(得分:0)
没有必要,有些人(包括我)认为这是不好的做法。
将其设置为NULL
的动机是,您可以在事后检查它是否已被删除,如果不是,则可以访问它。此外,这会阻止双重删除,因为NULL指针上的delete
是无操作。
另一方面,它可以隐藏错误。如果删除了对象,使用它没有意义,对吧?您应该知道该对象已被删除,而不是依赖于支票。
例如
if (p != NULL) //or just if (p)
p->doStuff()
为什么呢?你不知道它是否被删除了吗?不是清理逻辑的一部分吗?
答案 5 :(得分:0)
在您的代码示例中,没有明显的直接好处,但可以说是一个长期的维护成本优势。这个想法是,在删除试图取消引用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可能会被编译器优化,无论是对指针的赋值大多是无害的。对我来说,在专业发展的过程中,我始终保持谨慎。