【问题标题】:c++ Possible null pointer dereferencec++ 可能的空指针取消引用
【发布时间】:2011-05-27 08:07:09
【问题描述】:

我对一些代码运行 cppcheck 以查找可能的运行时错误。它报告了一个可能的空指针取消引用,情况如下:

Foo* x = ... //defined somewhere

...

Foo* y(x); //possible null pointer dereference.

编辑:更好的例子

for( int i = 0; i < N; i++ )
{
    Foo* x( ArrayOfObjsContainingFooPtr[i].FooPtr ); // line 3
    if( !x )                                         // line 4
        continue;
}

来自 cppcheck 的错误消息:

[C:\file.cpp:3]:(错误)可能为空 指针取消引用:x - 否则它 检查 x 是否为空是多余的 第 4 行

但我不明白这怎么可能。

【问题讨论】:

  • 你能发布一个更完整的例子吗?我怀疑存在导致这种情况的代码路径。请注意,静态代码分析工具并不完美,这可能是误报。
  • 这些例子都没有取消引用 x。
  • 您的示例仍然不完整。你能发布最小的可编译代码吗?上面的代码可以导致空指针取消引用。

标签: c++ pointers dereference cppcheck


【解决方案1】:

我真的很惊讶你收到了这个警告。对我来说,它的作用正好相反。使用从 Linux 中的源代码编译的 cppcheck 1.46.1。这很好:

struct Foo {
  int x;
};

struct Obj {
  Foo *FooPtr;
};

#define N 10

static Obj ArrayOfObjsContainingFooPtr[N];

int main() {
  for( int i = 0; i < N; i++ ) {
    Foo* x( ArrayOfObjsContainingFooPtr[i].FooPtr ); // line 3
    if( !x )                                         // line 4
      continue;
  }
}

现在,对于 this 循环体,根据 cppcheck 它也“很好”,尽管如果我真的尝试运行它会出现段错误,显然:

Foo* x( ArrayOfObjsContainingFooPtr[i].FooPtr ); // line 3
if (x->x == 0)
  break;
if( !x )                                         // line 4
  continue;

即使这样也“很好”:

int main() {
  Foo *p = 0;
  if (p->x == 0)
    return 1;

这最终会产生“可能的”空指针取消引用。可能,对吧:

int main() {
  Foo *p = 0;
  p->x = 0;

有趣的是,这虽然完全等同于前面的示例,但给出了明确的(不是“可能的”)空指针取消引用:

int main() {
  Foo *p = 0;
  if ((*p).x == 0)
    return 1;

结论:cppcheck 是一个非常有问题的工具。

【讨论】:

  • +1:用于展示此工具中与问题高度相关且显然不是很难找到的众多错误。
【解决方案2】:

采取以下措施:

Foo* x = ptr_foo; //ptr_foo is defined earlier in the code.

但是如果ptr_foo 被写入程序的另一个点,在另一个文件中呢?例如,假设您在someotherfile.c 中找到:

ptr_null = 0;

那么当y(x)被调用时,如果y取消对x的引用,那么Foo* x = ptr_foo;完全有可能导致不好的mojo。

根据我的经验,静态分析工具往往会报告大量误报,因为它们没有关于程序的任何状态信息。

如果您真的想确保不会遇到空指针引用,您可以尝试以下方法:

Foo* x = 0;
if(ptr_foo != 0){
    x = ptr_foo;
}else{
    x = //something else
}

【讨论】:

  • 代码示例的替代(恕我直言)版本:Foo* x = ptr_foo ? ptr_foo : /* something else */;
  • 不,“Foo* x = ptr_foo; 不可能是坏魔力”。这是一个指针副本,而不是取消引用。
  • 这就是我的想法,安德鲁
  • @Andrew: if "Foo* x = ptr_foo; //where ptr_foo = 0", then call "y(x) //假设 y dereferences x" 肯定是个坏魔咒。我发现我没有在回答中传达这一点,所以我更新了我的回答。
【解决方案3】:

对 Sergey Tachenov 的帖子进行总结:

 Foo* x( ArrayOfObjsContainingFooPtr[i].FooPtr ); // line 3
if (x->x == 0)
 break;
if( !x )                                         // line 4
 continue;

现在 cppcheck 可以正确检测到这个:

 $ cppcheck --enable=all nullptrderef9.cpp 
 Checking nullptrderef9.cpp...
 [nullptrderef9.cpp:20] -> [nullptrderef9.cpp:22]: (warning) Possible null pointer dereference: x - otherwise it is redundant to check it against null.

下一个例子也被正确检测到了:

int main() {
  Foo *p = 0;
  if (p->x == 0)
  return 1;
}

这是 cppcheck 的输出:

 $ cppcheck --enable=all nullptrderef10.cpp 
 Checking nullptrderef10.cpp...
 [nullptrderef10.cpp:19]: (error) Possible null pointer dereference: p

即使是下一个示例也表明 Cppcheck 可以按预期工作:

 int main()
 {
    Foo *p = 0;
    if ((*p).x == 0)
       return 1;
 }

这是输出:

$ cppcheck --enable=all nullptrderef11.cpp
  Checking nullptrderef11.cpp...
  [nullptrderef11.cpp:18]: (error) Possible null pointer dereference: p
  [nullptrderef11.cpp:18]: (error) Null pointer dereference

【讨论】:

    猜你喜欢
    • 2015-11-05
    • 1970-01-01
    • 1970-01-01
    • 2016-09-14
    • 1970-01-01
    • 2017-02-12
    • 2014-03-03
    • 1970-01-01
    • 2018-06-18
    相关资源
    最近更新 更多