1

将程序:

#include <stdio.h>

struct foo
{
   void blah()  {printf("blah\n");}
   int i;
};

void main(int, char**)
{
   ((foo*)NULL)->blah();
}

blah曾经在您知道的任何编译器上崩溃或执行除输出之外的任何操作吗?如果任何函数不访问任何成员(包括 vtable),当通过 NULL 指针调用时,它会崩溃吗?

关于这个主题还有其他问题,例如访问空指针上的类成员以及调用不通过空指针访问成员的非静态方法是否合法/定义明确的 C++?,并且总是指出这会导致未定义的行为。但这是在现实世界中未定义,还是仅在标准世界中?任何现存的编译器都没有按预期运行吗?你能想出任何合理的理由说明为什么未来的编译器不会按预期运行吗?

如果函数确实修改了成员,但 NULL ptr 被防范了怎么办。例如,

void foo::blah()
{
   foo* pThis = this ? this : new foo();
   pThis->i++;
}

编辑:为了记录,我想要这样做的原因是使我的链表类的接口尽可能简单和简洁。我想将列表初始化为 NULL 具有惯用的用法如下所示:

pList = pList->Insert(elt);
pList = pList->Remove(elt);
...

所有运算符都返回新的 head 元素。不知何故,我没有意识到使用容器类会使事情变得更容易,而且没有任何缺点。

4

3 回答 3

8

你能想出任何合理的理由说明为什么未来的编译器不会按预期运行吗?

一个有用的编译器可能会添加代码来访问调试版本中的真实对象,以期帮助您在开发周期的早期发现代码中的这个问题。

如果函数确实修改了成员,但 NULL ptr 被防范了怎么办。例如,

void foo::blah()
{
   foo* pThis = this ? this : new foo();
   pThis->i++;
}

由于使用空指针调用该函数是未定义的行为,因此编译器可以假设测试将始终通过并将该函数优化为:

void foo::blah()
{
   this->i++;
}

请注意,这是正确的,因为 ifthis不为 null,它的行为就像执行了原始代码一样,如果this为 null,它将是未定义的行为,编译器根本不需要提供任何特定的行为。

于 2013-07-01T21:51:30.500 回答
5

未定义的行为意味着您不能依赖将会发生的事情。但是,有时在调试时了解幕后发生的事情很有用,这样当不可能发生时您不会感到惊讶。

大多数编译器会将其编码为带有隐藏this参数的简单函数,如果this从未引用该参数,则代码将按预期工作。

检查this == NULL可能不起作用,具体取决于您的编译器优化的积极程度。由于一个格式良好的程序不可能有this==NULL,编译器可以自由地假装它永远不会发生并if完全优化该语句。我知道微软的 C++ 不会进行这种优化,因为它们的GetSafeHWND功能依赖于它按预期工作。

于 2013-07-01T21:30:13.647 回答
2

试图保护this == NULL不会给你任何真正想要的效果。主要是取消引用NULL指针 AFAIK 是undefined。对于不同的编译器,它的工作方式不同。假设它在一个场景中工作(比如这个)它不适用于这个场景这个(虚拟函数)。第二种和第三种情况是可以理解的,因为实例没有 vtable 条目来检查要调用哪个虚函数。但我不确定第一个是否可以这样说。

您需要考虑的另一件事是,任何无效指针也可能给出您想要防范的相同类型的错误,例如 this。请注意,它成功打印了 'Foo',然后尝试访问a. 这是因为指向的内存位置Test* t无效。在这里可以看到相同的行为,when Test* tis NULL

所以,一般来说,在你的代码中避免这样的行为和设计。这是不可预测的,如果有人追随您并更改您的代码,认为它应该像以前一样运行,则会导致不良影响。

于 2013-07-01T21:44:09.080 回答