8

如果我实际上没有访问取消引用的“对象”,那么取消引用空指针是否仍然未定义?

int* p = 0;
int& r = *p;    // undefined?
int* q = &*p;   // undefined?

一个更实际的例子:我可以取消引用空指针来区分重载吗?

void foo(Bar&);
void foo(Baz&);

foo(*(Bar*)0);  // undefined?

好的,根据标准,参考示例绝对是未定义的行为:

在定义良好的程序中不能存在空引用,因为创建此类引用的唯一方法是将其绑定到通过取消引用空指针获得的“对象”,这会导致未定义的行为

不幸的是,强调的部分是模棱两可的。是导致未定义行为的绑定部分,还是取消引用部分就足够了?

4

3 回答 3

6

我认为每个 C 程序员都应该知道的关于未定义行为的第二部作品可能有助于说明这个问题。

以博客为例:

void contains_null_check(int *P) {
  int dead = *P;
  if (P == 0)
    return;
  *P = 4;
}

可能会优化为(RNCE:冗余空检查消除):

void contains_null_check_after_RNCE(int *P) {
  int dead = *P;
  if (false)  // P was dereferenced by this point, so it can't be null 
    return;
  *P = 4;
}

将其优化为(DCE:Dead Code Elimination):

void contains_null_check_after_RNCE_and_DCE(int *P) {
  //int dead = *P; -- dead store
  //if (false)     -- unreachable branch
  //  return;
  *P = 4;
}

如您所见,即使从未使用过,简单的赋值dead也会导致未定义行为在程序中蔓延。int dead = *P

为了区分重载,我建议使用指针(可能为空)而不是人为地创建空引用并将自己暴露于未定义的行为。

于 2011-08-20T10:58:17.747 回答
5
int& r = *p;    // undefined?

我认为即使您实际上没有使用r(或*p)-取消引用的对象,您也有未定义的行为。因为在这一步之后(即取消引用空指针),程序的行为并不能由语言来保证,因为程序可能会立即崩溃,这是 UB 的一种可能性。您似乎认为只有读取r以便在实际用途中使用的值才会调用 UB。我不这么认为。

此外,语言规范明确指出“取消引用空指针的效果”会调用未定义的行为。它并没有实际使用来自空指针的取消引用对象的效果”调用 UB。取消引用空指针(或换句话说未定义的行为)的效果并不意味着您一定会立即遇到问题,或者它必须在取消引用空指针后立即崩溃。不,这只是意味着,在取消引用空指针未定义程序行为。也就是说,程序可能正如预期的那样,从头到尾正常运行。或者它可能会立即崩溃,或者在一段时间后 - 几分钟、几小时或几天之后。取消引用空指针,任何时候都可能发生任何事情。

于 2011-08-20T09:36:31.730 回答
4

是的,这是未定义的行为,因为规范说“左值指定一个对象或函数”(在第 3.10 节)并且它说对于*-operator“[取消引用] 的结果是一个左值,指的是对象或函数表达点”(第 5.3.1 条)。

这意味着没有描述取消引用空指针时会发生什么。这只是未定义的行为。

于 2011-08-20T15:01:09.863 回答