C++ 可能的空指针解引用

4

我对一些代码运行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 - 否则在第4行检查x是否为空是多余的

但我不知道这怎么可能发生。


3
你能否提供一个更完整的例子?我怀疑有一条代码路径导致了这种情况。请注意,静态代码分析工具并不完美,这可能是一个虚警。 - André Caron
这两个例子都没有对 x 进行解引用。 - Chris Hopman
你的示例仍然不完整。你能发布最小可编译代码吗?上面的代码可能会导致空指针引用。 - BЈовић
3个回答

3

我真的很惊讶你收到了那个警告。对我来说,它的作用恰好相反。在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;
  }
}

现在,根据cppcheck的检查结果,使用this循环体也是“好的”,虽然如果我实际运行它会导致段错误。
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 是一个非常有 bug 的工具。

展示了这个工具中众多与问题高度相关且显然不难发现的缺陷,值得加1。 - rubenvb

1

以下内容:

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

但是如果程序的另一个文件中的另一个位置写入了ptr_foo怎么办?例如,在someotherfile.c中,您会发现:

ptr_null = 0;

如果在调用y(x)时,Foo* x = ptr_foo;会导致不良影响,因为y会对x进行解引用,这是完全有可能的。

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

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

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

你的代码示例还有一个(我认为更清晰)的替代版本:Foo* x = ptr_foo ? ptr_foo : /* something else */; - Chris Lutz
不,"Foo* x = ptr_foo;"永远不可能出现问题。这是一个指针复制,而不是解引用。 - nobody
这就是我所思考的,Andrew。 - Glaeken
@Andrew:如果“Foo * x = ptr_foo; //其中ptr_foo = 0”,那么调用“y(x) //假设y解引用x”肯定会出问题。我看到我在我的答案中没有传达这一点,所以我更新了我的答案。 - David Weiser

0

对于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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接