为了账号安全,请及时绑定邮箱和手机立即绑定

空引用可能吗?

空引用可能吗?

C++
繁星coding 2019-11-28 10:36:04
这段代码是否有效(和定义的行为)?int &nullReference = *(int*)0;这两个g ++以及铛++编译它没有任何警告,即使使用-Wall,-Wextra,-std=c++98,-pedantic,-Weffc++...当然,该引用实际上并不为空,因为无法访问该引用(这意味着取消引用一个空指针),但是我们可以通过检查其地址来检查其是否为空:if( & nullReference == 0 ) // null reference
查看完整描述

3 回答

?
长风秋雁

TA贡献1757条经验 获得超7个赞

答案取决于您的观点:


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


但是,实际上,可以很容易地从空指针创建空引用,直到您真正尝试访问空引用后面的值时,您才会注意到。您的示例可能有点太简单了,因为任何好的优化编译器都会看到未定义的行为,并简单地优化掉所有依赖于它的内容(甚至不会创建null引用,它也会被优化掉)。


但是,优化工作取决于编译器来证明未定义的行为,这可能是不可能做到的。考虑一下文件内的这个简单函数converter.cpp:


int& toReference(int* pointer) {

    return *pointer;

}

当编译器看到此函数时,它不知道指针是否为空指针。因此,它只是生成将任何指针转换为相应引用的代码。(顺便说一句,这是一个小问题,因为指针和引用在汇编程序中是完全相同的野兽。)现在,如果您还有另一个user.cpp包含代码的文件


#include "converter.h"


void foo() {

    int& nullRef = toReference(nullptr);

    cout << nullRef;    //crash happens here

}

编译器不知道toReference()将取消对传递的指针的引用,并假定它返回有效的引用,实际上,该引用将为空引用。调用成功,但是当您尝试使用引用时,程序崩溃。希望。该标准允许发生任何事情,包括出现粉红色大象。


您可能会问为什么这是相关的,毕竟,内部已经触发了未定义的行为toReference()。答案是调试:空引用可能像空指针一样传播和扩散。如果您不知道可以存在空引用,并学会避免创建空引用,则可能要花费大量时间来弄清楚为什么仅在尝试读取普通旧int成员时成员函数似乎崩溃了(答案:实例在该成员的调用中是一个空引用,this一个空指针也是如此,并且您的成员被计算为位于地址8)。


那么如何检查空引用呢?你给了电话


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

在你的问题。嗯,那是行不通的:按照标准,如果取消引用空指针,您将具有未定义的行为,并且不创建空引用就无法创建空引用,因此,空引用仅存在于未定义行为的领域内。由于编译器可能会假设您未触发未定义的行为,因此它可以假设不存在空引用(即使它很容易发出生成空引用的代码!)。这样,它看到了if()条件,得出结论说它不可能成立,而只是丢弃了整个if()语句。随着链接时间优化的引入,以鲁棒的方式检查空引用已变得毫无可能。


TL; DR:

空引用有些可怕地存在:


它们的存在似乎是不可能的(按标准而言),

但是它们却存在(按生成的机器代码来确定),

但是您看不到它们是否存在(=您的尝试将被优化),

但无论如何它们可能会杀死您(=您的程序在怪异的点崩溃,甚至更糟)。

您唯一的希望是它们不存在(=编写您的程序以不创建它们)。


我希望那不会困扰您!


查看完整回答
反对 回复 2019-11-28
?
凤凰求蛊

TA贡献1825条经验 获得超4个赞

如果您打算找到一种在单例对象枚举中表示空值的方法,那么(取消)引用空值(C ++ 11,nullptr)是一个坏主意。


为什么不按如下所示在类中声明表示NULL的静态单例对象,并添加一个返回nullptr的强制转换指针运算符?


编辑:更正了几种错误类型,并在main()中添加了if语句,以测试实际工作的强制转换指针操作符(我忘了..我的错)-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;

  }

}


查看完整回答
反对 回复 2019-11-28
  • 3 回答
  • 0 关注
  • 826 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信