这条代码是否有效(和定义的行为)?

int &nullReference = *(int*)0;

G ++和clang ++都在没有任何警告的情况下对其进行编译,即使使用 -Wall, -Wextra, -std=c++98, -pedantic, -Weffc++...

当然,由于无法访问该引用实际上并非零(这意味着要删除无效指针),但是我们可以通过检查其地址检查是否为null:

if( & nullReference == 0 ) // null reference
有帮助吗?

解决方案

参考不是指针。

8.3.2/1:

参考应初始化以引用有效的对象或功能。 [注意:尤其是,在明确定义的程序中不能存在零引用,因为创建此类参考的唯一方法是将其绑定到通过删除无效指针获得的“对象”,这会导致不确定的行为。如9.6中所述,参考不能直接绑定到位场。这是给予的

1.9/4:

该国际标准中描述了某些其他操作是未定义的(例如,取消无效指针的效果)

正如约翰内斯在一个删除的答案中所说的那样,人们怀疑是否应该明确地将“删除无效指针”为未定义的行为。但这不是引起疑问的案例之一,因为无效指针当然没有指向“有效的对象或功能”,并且在标准委员会内没有愿意引入无参考。

其他提示

答案取决于您的观点:


如果您根据C ++标准进行判断,则无法获得零引用,因为您首先获得了不确定的行为。在不确定行为的第一次发病率之后,该标准允许发生任何事情。所以,如果你写 *(int*)0, ,从语言标准的角度来看,您已经具有未定义的行为,从而取消了无效指针。该程序的其余部分无关紧要,一旦执行此表达式,您就会退出游戏。


但是,实际上,可以轻松地从零指针创建null引用,直到您实际尝试访问NULL引用背后的值之前,您才会注意到。您的示例可能太简单了,因为任何良好的优化编译器都会看到未定义的行为,并且只需优化任何依赖于它的内容(甚至都不会创建零引用,它将被优化)。

然而,优化取决于编译器以证明不确定的行为,这可能是不可能的。考虑文件中的这个简单功能 converter.cpp:

int& toReference(int* pointer) {
    return *pointer;
}

当编译器看到此功能时,它不知道指针是否为空指针。因此,它只是生成代码,将任何指针变成相应的参考。 (顺便说一句:这是一个NOOP user.cpp 使用代码

#include "converter.h"

void foo() {
    int& nullRef = toReference(nullptr);
    cout << nullRef;    //crash happens here
}

编译器不知道 toReference() 将取消传递的指针,并假设它返回有效的参考,这将是实践中的无效参考。呼叫成功,但是当您尝试使用参考时,程序会崩溃。希望。该标准允许发生任何事情,包括Ping Elephants的外观。

您可能会问为什么这是相关的,毕竟,未定义的行为已经被触发了 toReference(). 。答案是调试:零引用可能像无效的指针一样传播和扩散。如果您不知道可以存在零引用并学会避免创建它们,那么您可能会花很多时间试图弄清楚为什么您的会员函数似乎只是试图读取普通的旧旧 int 成员(答案:会员呼叫中的实例是无用的参考,因此 this 是一个空指针,您的成员被计算为地址8)。


那么检查null引用呢?你给了线

if( & nullReference == 0 ) // null reference

在你的问题中。好吧,这将无法使用:根据标准,如果您放下无效指针,则您的行为不确定,并且不能在不使用零指针的情况下创建零引用,因此仅在未定义行为的领域内才存在null引用。 由于您的编译器可能会假定您没有触发不确定的行为,因此可以假设没有诸如null引用之类的东西 (即使它将很容易发出生成零引用的代码!)。因此,它看到了 if() 条件得出的结论是不可能是真实的,只是扔掉了整个 if() 陈述。随着链接时间优化的引入,很难以强大的方式检查无效的引用。


tl; dr:

零引用有些可怕:

它们的存在似乎是不可能的(=按标准),
但是它们存在(=通过生成的机器代码),
但是您看不到它们是否存在(=您的尝试将被优化),
但是他们可能无论如何都可能杀死您(=您的程序在怪异的地方崩溃,或更糟)。
您唯一的希望是它们不存在(=编写您的程序以不创建它们)。

我确实希望不会困扰您!

如果您的目的是找到一种在单顿对象列举中表示null的方法,那么(de)参考null(it c ++ 11,nullptr)是一个坏主意。

为什么不声明在类中代表NULL的静态单例对象如下,并添加返回NULLPTR的铸件对销操作员?

编辑:纠正了几个错误,并在main()中添加了IF-Statement,以测试铸件对表操作员实际工作(我忘记了..我的坏) - 2015年3月10日 -

// Error.h
class Error {
public:
  static Error& NOT_FOUND;
  static Error& UNKNOWN;
  static Error& NONE; // singleton object that represents null

public:
  static vector<shared_ptr<Error>> _instances;
  static Error& NewInstance(const string& name, bool isNull = false);

private:
  bool _isNull;
  Error(const string& name, bool isNull = false) : _name(name), _isNull(isNull) {};
  Error() {};
  Error(const Error& src) {};
  Error& operator=(const Error& src) {};

public:
  operator Error*() { return _isNull ? nullptr : this; }
};

// Error.cpp
vector<shared_ptr<Error>> Error::_instances;
Error& Error::NewInstance(const string& name, bool isNull = false)
{
  shared_ptr<Error> pNewInst(new Error(name, isNull)).
  Error::_instances.push_back(pNewInst);
  return *pNewInst.get();
}

Error& Error::NOT_FOUND = Error::NewInstance("NOT_FOUND");
//Error& Error::NOT_FOUND = Error::NewInstance("UNKNOWN"); Edit: fixed
//Error& Error::NOT_FOUND = Error::NewInstance("NONE", true); Edit: fixed
Error& Error::UNKNOWN = Error::NewInstance("UNKNOWN");
Error& Error::NONE = Error::NewInstance("NONE");

// Main.cpp
#include "Error.h"

Error& getError() {
  return Error::UNKNOWN;
}

// Edit: To see the overload of "Error*()" in Error.h actually working
Error& getErrorNone() {
  return Error::NONE;
}

int main(void) {
  if(getError() != Error::NONE) {
    return EXIT_FAILURE;
  }

  // Edit: To see the overload of "Error*()" in Error.h actually working
  if(getErrorNone() != nullptr) {
    return EXIT_FAILURE;
  }
}

clang ++ 3.5甚至警告它:

/tmp/a.C:3:7: warning: reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to
      always evaluate to false [-Wtautological-undefined-compare]
if( & nullReference == 0 ) // null reference
      ^~~~~~~~~~~~~    ~
1 warning generated.
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top