今天又发生了与同事的争执。他们认为这样的代码没有问题,并且在任何地方都可以正常工作:
#include <iostream>
struct S{
int a;
void foo(){
std::cout << "hello";
}
};
int main(){
S *p = nullptr;
p->foo(); //hello
}
他们说我们不访问数据 => 我们没有爬入地址 0 的内存 => 没有问题。我用嘴巴向他们证明,如果你调用任何非静态方法,nullptr
这将立即成为未定义的行为,在这个方法中发生什么并不重要。
问题:
- 谁是对的?
- 它在标准中写在哪里?
- 标准中是否有关于如何实施的内容
this
?
在“经典”C++ (C++98) 中,情况是明确的——取消引用空指针会导致未定义的行为。因此,通过空指针调用对象的非静态方法会导致未定义的行为。此方法是否访问类成员都没有关系。这是语言规范的立场。从这个角度来看,你是绝对正确的,你的对手关于“一切都将在任何地方工作”这个话题的论点只不过是“街头教育”类别中“我看汇编程序
书,我明白了”的结果无花果。”同时,在这个问题上,为了形成更灵活/更精细的规范,已经进行了相当长的一段时间的尝试。尤其是
DR#232:是否通过空指针进行间接未定义行为?
然而,这方面的工作自 2005 年以来一直处于起草状态。老实说,似乎没有人可以对当前标准的文本对这个话题给出任何可以理解的解释,也许正是因为这个话题仍然“悬而未决”。
如您所知,该标准不会为您的特定情况创建单独的规范。一旦我们转向更一般的情况,那么
this
在[多重]继承的条件下调用方法时立即出现诸如指针转换之类的情况。不清楚编译器
this
在调用基类方法的过程中转换指针时是否必须遵守“null转为null”的规则?正是由于这些微妙之处,最初做出的决定是禁止通过空指针调用非静态方法。现在将会(和现在)什么以及该语言的作者的意图是什么 - 我们必须等待并弄清楚。请注意,顺便说一下,8.5.1.2/4要求调用类方法时的隐藏参数使用显式类型转换
this
进行初始化。也就是说,在上面的多重继承示例中,调用中的类型指针必须初始化为. 此转换根据 null 到 null 规则进行,结果也将是一个空指针。但是,在调用时内部的 GCC 和 Clang中, . 也就是说,这些编译器不符合 8.5.1.2/4 的要求。这立即表明 GCC 和 Clang 仍将此类调用视为未定义行为。c->foo()
this
B
(B *) c
(B *) c
this
foo
0x4
PS 同时,在 C 语言中,空指针的解引用是被严格禁止的。
编码
与此相同
所以打电话
相当于这个
要检查,只需查看 CPU 窗口。两个调用将生成相同的代码
那些。只要你不解决
this
就没有问题。调用this
将在两种情况下发生this
指针根本不传递给静态函数。