我正在用-O0(最近的gcc / clang)编译以下内容,他们都给了我一个我没想到的答案。
#include <iostream>
struct xy{
int x,y;
};
int main()
{
xy a{1,2};
int x{1};
int y{2};
int *ptr1=&a.x;
int *ptr2=&x;
ptr1++; // I now point to a.y!
(*ptr1)++; // I now incremented a.y to 3
ptr2++; // I now point to y!
(*ptr2)++; // I now incremented y to 3
std::cout << "a.y=" << a.y << " ptr1=" << *ptr1 << '\n';
std::cout << "y= " << y << " ptr2=" << *ptr2 << '\n';
}
输出:
a.y=3 ptr1=3
y= 2 ptr2=2
因此,编译器正在优化对非类变量指针的访问。
我还尝试将int和int *标记为volatile,但它没有任何区别。
我遗漏了标准的哪一部分/为什么允许编译器执行此操作?
Coliru片段:http://coliru.stacked-crooked.com/a/ed0757a6621c37a9
答案 0 :(得分:4)
在处理类成员的第一种情况下,您忽略的部分是允许编译器在对象的成员之间和对象的末尾添加任意数量的填充。由于这种增量,指向一个成员的指针不必为您提供下一个成员。
您缺少的标准的第二部分是通过指向它没有指向的内容来访问内存是非法的。即使y
可能存在于内存中,也不允许指针访问它。允许访问x
,并且可以进行比较以查看它是否超过x
但是它无法取消引用过去的x
地址。
答案 1 :(得分:3)
指针算法仅在数组中有效。您无法通过插入指向y
的指针来x
。您的程序的行为是 undefined 。你的陈述
ptr1++; // I now point to a.y!
完全错了。请记住,允许编译器在struct
的元素之间插入任意数量的填充。
更详细地说,您可以将指针设置为超过标量地址的指针,但不允许取消引用它。