【问题标题】:Is there any point to temporarily making a pointer NULL?暂时将指针设为 NULL 有什么意义吗?
【发布时间】:2013-01-28 19:53:34
【问题描述】:

我见过很多这样的代码:

SomeType* ptr = NULL;
ptr = SomeMethod(some, params);

有什么意义?我还看到ptr 在其他地方声明(例如在类定义中),然后在类构造函数中会有这样的:

ptr = NULL;
ptr = SomeMethod(some, params);

我不明白为什么要这样做。 ptr = NULL 行肯定没用吗?

【问题讨论】:

  • 没错,没用。最好说SomeType * ptr = SomeMethod(some, params);
  • 有些程序员有点偏执,似乎相信如果他们不点每一个 i 并交叉每一个 t 一些竞态条件会神奇地出现并炸毁一切。我认为这只是过于狂热的防御性编程。
  • 在 C89 中,您有时必须将声明与赋值分开,我理解为什么有人不想让事情未初始化。
  • 如果这两行之间没有其他代码,那么根本没有意义。至少在 C++ 中。
  • @Chris 在这种特殊情况下,您是对的。这两行应该合并。一般来说,这种行为与妄想症无关。这是防御性编程的一种表现形式。我见过太多人修改代码并突然使用未初始化的变量。非常丑陋的影响由此产生。 (尤其是开启优化)

标签: c++ c pointers null


【解决方案1】:

如果“SomeMethod”引发异常,您的 SomeType* 将继续指向您不希望它指向的内容。因此,如果您不想再指向旧的东西,将指针设置为 null 绝对是一个好习惯。

【讨论】:

  • 这在 C++ 中是正确的
  • @trumpetlicks C++ 解决方案是拥有某种智能指针,在退出范围时采取一些明智的行动。
  • @juanchopanza - 是的,但即使在你的例子中,你仍然会采取类似的无效方法。您可能真的希望设置为 NULL 有硬件原因。在没有 ECC 的系统的情况下,如果发生单个事件扰乱并且该未初始化的值以某种方式作为下一个要运行的指针出现故障等,则可能会发生异常......很多时候存储指针的 RAM 位置来了处于元值状态(即未归零)。安全实践不希望在无人区具有价值。不过有趣的问题:-)
  • 这仅适用于 C++ 中的 assignment。在初始化的情况下,将退出指针变量的范围,可能的未初始化值不是问题。
  • ... 在赋值的情况下,你必须想知道为什么在创建新指针之前丢弃旧指针。对于异常安全的代码来说,这是一个糟糕的设计。相反,分配新指针(在异常情况下没有任何改变),然后交换指针(不能抛出),然后取消分配旧对象(也不能抛出)。结果,您要么拥有旧状态,要么拥有新状态,但永远不会有 NULL 指针。
【解决方案2】:

除了适用于 C++ 的抛出异常的优点之外,即使在 C 中,初始化所有变量也是一个好主意。当编译器可以确定不需要初始化时,它几乎总是会优化初始化。

这里有一个小例子,说明如果你不这样做会发生什么。

SomeType* ptr;
ptr = SomeMethod(some, params);

如您所见,这工作正常,ptr 没有有效值没有问题。

现在,有人认为添加更多代码是个好主意:

SomeType* ptr;

int x = someotherfunction;

if (x > 90)
{
     ptr = SomeMethod(some, params);
}

现在你的ptr 突然之间会有一些随机的废话内容 - 可能是 NULL,但很可能是别的东西。是的,我们都认为我们会记得回来查看我们的更改是否影响了其他东西,等等。但是如果你已经编程了几年,我几乎可以肯定你有一些案例“哎呀,没想到”。

想象一下,我添加“someotherfunction”的新代码是 20 或 30 行,您可以清楚地看到问题所在。

【讨论】:

  • 我认为比较讨论是用Kerrek SB在评论“SomeType * ptr = SomeMethod(some, params);”中提到的方法,而不是让ptr未分配。
  • @ArunSaha 时不时我会发现一些无法进行初始化的情况,因为需要先计算一些参数,而 C89 不允许您在任何地方声明变量。因此,您必须声明它们并稍后分配它们。我们无法从 OP 的抽象示例中分辨出上下文是什么。因此,我觉得重要的是不要直截了当地说它没用,因为有时是有原因的。
【解决方案3】:

在第一种情况下,最好在声明新值时始终分配一个值,这样您就不会得到统一的值(保护您免受自己的错误影响)。但是如果直接一个接一个的话,就可以认为是没用了。

第二种情况,完全没用。

【讨论】:

    【解决方案4】:

    确实没用(这被称为“死任务”)。但是,有些人更喜欢在声明时初始化指向NULL 的指针,然后分别显式地分配给它。也许这只是出于可读性的原因(也许有些人认为它更具可读性或发现即使在例如赋值被注释掉以进行调试时声明变量也很有用)。我不确定我是否会这样做/将其视为练习。

    另外请注意,大多数编译器会自动优化赋值。

    【讨论】:

      【解决方案5】:

      这种做法不仅没有用;这是有害的。如果你只是写:

      SomeType* ptr = NULL;
      /* lots of stuff in between */
      ptr = SomeMethod(some, params);
      

      并且不小心在中间代码的某处使用了ptr,编译器将无法通知您此错误。如果你写:

      SomeType* ptr;
      /* lots of stuff in between */
      ptr = SomeMethod(some, params);
      

      那么在赋值之前任何使用ptr都是UB,一个好的编译器或者静态分析工具会通知你这个错误。

      【讨论】:

      • 我不同意未初始化的变量更好,当你有一个非常好的域值断言“我不指向任何东西”时。这种“任何用途”的业务纯粹是学术性的。一个好的工具还可以通知您在分配之前对ptr 的某些取消引用使用涉及空值。如果该工具在其数据流分析中足够智能以检测未初始化的使用,则它可以足够智能以了解空指针值到达的位置。尤其是静态的,从显式初始化中可以明显看出。
      【解决方案6】:

      现在的代码没有用,但也许使用预期将来的维护,其中将在冗余初始化和分配之间添加更多行,以便初始化不再是冗余的。

      代码也可能是删除的结果。也就是说,在声明和赋值之间可能存在一些代码,但后来被删除了,并且没有进行清理以将赋值合并到初始化中。当然,没有 point 表明代码是这样的。在这种情况下,我们只有一个历史解释来说明它是如何变成这样的(也许还有理由改变它)。

      有时看似无用的变量初始化是由于有人不得不使编译器对“可能未使用的”变量发出警告而导致的,如果有不止一种方法可以到达使用该变量的代码块,就会发生这种情况, 并且编译器还不够复杂,无法证明在所有情况下变量都是通过赋值来初始化的。

      在声明它们的地方初始化所有变量是一个好习惯,因为它减少了由于未初始化数据而导致的不可预测行为的可能性。一些人认为,当未初始化的变量被赋予不适当值的初始化所消除时,某些静态工具的工作效果会较差,从而使错误更难被发现。但是,如果错误基于初始化数据,则它们更可重复,并且错误行为也很可能在平台之间可移植。 某些语言会为您初始化本地人。在 Lisp 中,(let (x) ...) 表示在代码块... 中,变量x 的值是NIL,因为没有初始化程序。 Lisp 代码的静态分析可以很容易地追踪诸如 NIL 被不恰当地传递给数值函数等事实。在 C 和相关语言中,问题有点不同,因为对于像 int 这样的类型,没有像 NIL 这样的错误类型值的等价物。所以0 被替换,这是一个有效值,不会简单地因类型不匹配而破坏计算。不恰当的0 可能会导致计算中出现算法错误。然而,在指针的情况下,空指针值是NIL 的合理复制品,因为它是程序员可见的、可移植的域值,上面写着“我不指向任何对象”。当指针实际上暂时没有指向任何东西时,我们应该给它这个值,而不是在其中留下可能意外指向意外对象的随机位。

      【讨论】:

        猜你喜欢
        • 2010-10-16
        • 2011-12-04
        • 1970-01-01
        • 2018-02-09
        • 1970-01-01
        • 1970-01-01
        • 2011-01-17
        • 1970-01-01
        相关资源
        最近更新 更多