为什么C++不允许从有效地址和类型创建有效指针?

Dun*_*eal 2 c++

如果你知道两条信息:

  1. 内存地址.
  2. 存储在该地址中的对象类型.

然后你逻辑上拥有引用该对象所需的一切:

#include <iostream>
using namespace std;

int main()
{
    int x = 1, y = 2;
    int* p = (&x) + 1;
    if ((long)&y == (long)p)
        cout << "p now contains &y\n";
    if (*p == y)
        cout << "it also dereference to y\n";
}
Run Code Online (Sandbox Code Playgroud)

但是,根据C++标准,这是不合法的.它适用于我尝试过的几个编译器,但它是未定义的行为.

问题是:为什么?

pad*_*ddy 5

如果要将指针视为数字类型,首先需要使用std::uintptr_t,而不是long.这是第一个未定义的行为,但不是您正在谈论的行为.

它适用于我尝试过的几个编译器,但它是未定义的行为.

问题是:为什么?

好的,所以当我调用这个未定义的行为时,评论部分就消失了.它实际上是未指定的行为(也称为实现定义).

您正在尝试比较两个明显不相关的指针:

  • &x + 1
  • &y

指针&x+1是一个一个接一个的指针.该标准允许您使用这样的指针,但只有在您使用它来与基于指针进行比较时才会定义行为x.如果将其与其他任何内容进行比较,则不会指定该行为:[expr.eq§3.1]

编译器可以自由放置y它选择的任何地方,包括寄存器.因此,无法保证&y&x+1相关.

作为一个想要表明这是否实际上是未定义行为的人的练习,也许从这里开始:

  • [basic.stc.dynamic.safety§3.4]:

    整数值是安全派生指针的整数表示,只有它的类型至少与std :: intptr_t一样大,并且它是以下之一:...

    3.4加法或按位运算的结果,其中一个操作数是安全派生指针值P的整数表示,如果由reinterpret_cast转换的结果将等于可从reinterpret_cast(P)计算的安全派生指针.

  • [basic.compound§3.4]:

    注意:超出对象末尾的指针([expr.add])不被视为指向可能位于该地址的对象类型的无关对象


T.C*_*.C. 5

它会对优化造成严重破坏.

void f(int* x);

int g() {
    int x = 1, y = 2;
    f(&x);
    return y;
}
Run Code Online (Sandbox Code Playgroud)

如果能有效方式"猜测"的地址y来自x的地址,然后调用f可以修改y,因此该return语句必须重新加载的值y从内存中.

现在考虑一个具有更多局部变量和更多其他函数调用的典型函数,在每次调用之前必须将每个变量的值保存到内存中(因为被调用的函数可以检查它们)并在每​​次调用后重新加载它们(因为被调用的函数可能已经修改过它们).