考虑以下示例:
// someLibrary.h which is exported.
struct HandlePrivate;
typedef HandlePrivate& Handle;
Handle getHandle(int code);
void closeHandle(Handle handle);
// someLibrary.cpp
#include "someLibrary.h"
struct HandlePrivate{};//definition of HandlePrivate;
Handle getHandle(int code)
{
static HandlePrivate instance;
return code >= 0
? instance
: ( *(HandlePrivate*)0); // Hack, I know it's UB.
}
现在,其他程序员使用这个库:
// userSource.cpp
#include "someLibrary.h"
void foo(int code)
{
Handle h = getHandle(code);
// some user code
closeHandle(h);
}
问题:如果"SomeLibrary"作者使用该hack (UB),何时可能会破坏用户代码(崩溃或其他糟糕的东西)?
如果用户只使用Windows和Visual Studio 2010,答案会改变吗?
因为,下面的例子没有崩溃!!:
#include <functional>
#include <type_traits>
#include "someLibrary.h"
void foo(int code)
{
typedef std::reference_wrapper< std::remove_reference<Handle>::type> reference;
reference h = getHandle(code);
closeHandle(h); // is not crash, if code = -1 !!!!!!!!!!
//但是,std::reference_wrapper::operator &() -对指向0的指针解引用。}
当指针被设置为0时,它指向地址为0的对象。地址可以是物理地址0或逻辑地址(相对于您的用户空间)。
对指向0的指针解引用的唯一情况是,当地址0有内存,并且地址0可以被应用程序访问(一些操作系统不允许访问各种地址)。
例如,在某些嵌入式系统中,地址0是一个包含启动程序地址的向量。要读取地址向量的值,必须赋值一个值为0的指针,并对其解引用。
使用空指针访问对象的方法是未定义的行为。如果该方法不使用任何数据成员,您可能会很幸运。如果方法使用数据成员,则该方法期望这些数据成员位于地址0附近。
如果你想在不创建对象实例的情况下访问对象方法,那么将这些方法声明为static
。