指针可互换性与具有相同的地址

n. *_* m. 24 c++ arrays pointers language-lawyer c++17

标准N4659工作草案说:

[basic.compound]
如果两个对象是指针可互换的,那么它们具有相同的地址

然后注意到

数组对象及其第一个元素不是指针可互换的,即使它们具有相同的地址

使数组对象及其第一个元素非指针可互换的基本原理是什么?更一般地说,区分指针 - 可互换性概念与具有相同地址的概念的理由是什么?在那里某处不存在矛盾吗?

看来,给定这一系列陈述

int a[10];

void* p1 = static_cast<void*>(&a[0]);
void* p2 = static_cast<void*>(&a);

int* i1 = static_cast<int*>(p1);
int* i2 = static_cast<int*>(p2);
Run Code Online (Sandbox Code Playgroud)

p1 == p2但是,我们已经i1明确定义并且使用i2会导致UB.

T.C*_*.C. 20

显然存在基于此优化的现有实现.考虑:

struct A {
    double x[4];
    int n;
};

void g(double* p);

int f() {
    A a { {}, 42 };
    g(&a.x[1]);
    return a.n; // optimized to return 42;
                // valid only if you can't validly obtain &a.n from &a.x[1]
}
Run Code Online (Sandbox Code Playgroud)

考虑到p = &a.x[1];,g可能试图获得访问a.nreinterpret_cast<A*>(reinterpret_cast<double(*)[4]>(p - 1))->n.如果内部强制转换成功地产生了一个指向a.x,那么外部强制转换将产生一个指针a,给予类成员访问定义的行为,从而禁止优化.

  • 有趣的。什么编译器会这样做?我在 godbolt.org 上没有找到任何信息。 (3认同)
  • @MM仅仅因为它代表相同的地址(“指向相同的字节”)并不意味着它在抽象机中具有相同的指针值。内部转换的结果是一个类型为“指向 4 个‘double’数组的指针”的指针,其值为“指向‘ax’第一个元素的指针”,因此外部转换的结果是一个类型为“pointer”的指针到“A””,其值为“指向“ax”的第一个元素的指针”,并且由于它实际上并不指向“A”对象,因此当使用类成员访问表达式访问非`A` 的静态数据成员。 (3认同)
  • @TC根据你的最后评论,是否正确的说核心理由(这个问题是问的)是这样的,指向数组元素的指针不能"逃避"他们的数组.与`void h(double(*)[4])比较; h(&a.x);` - 可能是优化不再可能,因为`h`可能会将其参数转换为'A*`; 该转换是正确的,因为指向标准布局结构的指针可与指向其第一个元素的指针互相转换. (3认同)
  • [basic.lval]/11意义上的"严格别名"无关紧要.`reinterpret_cast <A*>(reinterpret_cast <double(*)[4]>(p-1)) - > n`在该规则的含义内不执行"访问".至于你的替换表达式,在任何地方都没有`char`数组,指针算术是未定义的. (2认同)