如果我实际上没有访问取消引用的“对象”,那么取消引用空指针仍然未定义吗?
int* p = 0;
int& r = *p; // undefined?
int* q = &*p; // undefined?
一个稍微更实际的例子:我可以取消引用空指针来区分重载吗?
void foo(Bar&);
void foo(Baz&);
foo(*(Bar*)0); // undefined?
好吧,根据标准,参考示例绝对是未定义的行为:
空引用不能存在于定义明确的程序中,因为创建此类引用的唯一方法是将其绑定到通过取消引用空指针获得的“对象”,这会导致未定义的行为。
不幸的是,强调的部分含糊不清。是绑定部分导致了未定义的行为,还是解除引用部分就足够了?
我认为每个 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
赋值导致了程序中出现未定义的行为。
为了区分重载,我建议使用指针(可能为空),而不是人为创建空引用并使自己暴露于未定义的行为。
int& r = *p; // undefined?
我认为这里你有未定义的行为,即使你不实际上使用
r
(或*p
)-取消引用的对象。因为在这一步(即取消引用空指针)之后,程序行为不再由语言保证,因为程序可能会立即崩溃,这也是 UB 的可能性之一。您似乎认为只有读取 r
的值才能在 real 中使用 才能调用 UB。我不这么认为。
此外,语言规范明确指出“取消引用空指针的效果”会调用未定义的行为。它确实不是说“实际上使用从空指针取消引用的对象的效果”调用UB。取消引用空指针的效果(或者换句话说,“未定义行为”)并不意味着您将“必然且立即”遇到问题,或者在取消引用空指针后必须“立即”崩溃。不。这只是意味着,在取消引用空指针之后,程序行为并未定义。也就是说,程序可能正常运行,正如预期的那样,从开始到结束。或者它可能会立即崩溃,或者在一段时间后(几分钟、几小时或几天后)崩溃。 随时 在取消引用空指针之后,任何事情都可能发生。
是的,这是未定义的行为,因为规范说“左值指定一个对象或函数”(第 3.10 条),并且对于 *
运算符,“[解除引用]的结果是引用该对象的左值或表达式指向的函数”(第 5.3.1 节)。
这意味着没有描述取消引用空指针时会发生什么。这只是未定义的行为。