【问题标题】:Why is Pointer = NULL defined as clean code?为什么 Pointer = NULL 被定义为干净的代码?
【发布时间】:2021-06-10 19:09:48
【问题描述】:

所以我的编程老师告诉我们,如果你不使用指针但喜欢声明它,最好用 NULL 来初始化它。如果我什至不使用它,它如何防止任何错误?

或者如果我错了,这样做有什么好处?

【问题讨论】:

  • 一般来说初始化变量是个好主意:-)
  • 即使您不使用它,初始化它的好处也会在您更改程序并使用它时出现。当然,如果你从不使用它,也许它不应该存在。

标签: c pointers null-pointer


【解决方案1】:

取消引用 NULL 可能(保证?)导致分段错误,立即使您的应用崩溃并提醒您刚刚执行的不安全内存访问。

保持指针未初始化将意味着它仍然具有该内存的前一个用户留下的任何垃圾。它完全有可能成为指向应用程序中真实内存区域的指针。取消引用它将是未定义的行为,可能会导致段错误,也可能不会。后者是最坏的情况,你应该担心。你的应用会随着由此产生的任何无意义的行为而继续运行。

这是一个演示:

#include <stdio.h>
#include <stdlib.h>

void i_segfault() {
    fflush(stdout); // Flush whatever is left of STDOUT before we blow up
    
    int *i = NULL;
    printf("%i", *i); // Boom
}

void use_some_memory() {
    int *some_pointer = malloc(sizeof(int));
    *some_pointer = 123;
    printf(
        "I used the address %p to store a pointer to %p, which contains %i\n",
        &some_pointer, some_pointer, *some_pointer
    );
}

void i_dont_segfault() {
    int *my_new_pointer; // uninitialized
    
    printf(
        "I re-used the address %p, which still has a lingering value %p, which still points to %i\n",
        &my_new_pointer, my_new_pointer, *my_new_pointer
    );
}

int main(int argc, char *argv[]) {
    use_some_memory();
    i_dont_segfault();
    
    i_segfault();
}

【讨论】:

  • 不确定。此外,某些系统上的地址0 甚至可能是可以访问的有效地址。好吧,在这种情况下,NULL 应该通过其他方式定义,但实际上不会发生。
  • @EugeneSh。但是在那些系统上,NULL 仍然是0 吗?或者它是不是故意不可取消引用的其他等效值?
  • 如果 NULL0 被分配给一个指针,编译器需要用一些无效值填充它,实际上可能是也可能不是 0。同样,与NULL0 的比较将始终与无效值进行比较。
  • @MooingDuck:空指针有一个有效值;完全允许和定义指针的值是空指针。它是一个保证与任何指向任何对象或函数的指针比较不相等的值。
  • 我想你可以说的是,许多常用的实现会导致大多数尝试取消引用NULL指针时出现段错误。这仍然非常有用,即使它不是保证。
【解决方案2】:

至少有两种方法可以确保您的程序不使用未初始化的指针:

  • 您可以仔细设计和编写您的程序,以确保程序控制永远不会流经使用指针而不先初始化它的路径。
  • 您可以将指针初始化为NULL

第一个可能很困难,具体取决于程序,并且人类不断犯错误。第二个很简单。

另一方面,第二种方法只解决了一个问题:它确保如果您使用指针而不以其他方式对其进行初始化,它将具有分配的值。此外,在许多系统中,它确保如果您尝试使用该值来访问指向的对象,您的程序将崩溃而不是做更糟糕的事情,例如损坏数据并产生错误结果或删除有价值的信息。

这是一个有用的问题要解决,因为这意味着在测试期间很可能会发现未能分配所需值的错误,即使没有,可能造成的损害也可能是有限的。但是,这并没有解决确保指针在使用之前被分配所需值的问题。因此,与许多这些代码建议一样,它是帮助限制人为错误的有用提示,但它不是一个完整的解决方案。

【讨论】:

  • 感谢您的详细解答!我仍然不确定这些指针的意义是什么。你怎么能一开始就使用一个未初始化的指针呢?
【解决方案3】:

如果您不初始化变量(错误地),问题是您的程序可能比正常程序更加异常。假设您声明了一个 enum,其值为 ONETWOTHREE,但您忘记初始化它,并且在某些时候您包含以下代码:

switch(my_var) {
case ONE: /* do one */
    ...
    break;
case TWO: /* do two */
    ...
    break;
case THREE: /* do three */
    ...
    break;
}

而且您可能会发疯,因为您假设至少应该执行该类型的可能值之一......但这根本不是真的,因为变量尚未初始化并且该值甚至不需要遵守对其所代表的数据类型施加的约束。

初始化变量可以在您的代码中引入一些简短的指令,但它可以在搜索错误时避免很多噩梦。

在指针未初始化的情况下,情况更糟,因为很多程序员free()根据测试从堆中分配内存

if (var) free(var);

并且很可能一个未初始化的指针类型的自动变量将指向某个地方,并且仍会尝试为free()d。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    • 2014-02-09
    • 2010-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多