[编辑:这是动机:将变量的指针传递给外部函数可能会意外中断对“相邻”变量的某些优化,因为可能会获得外部函数从原始指针计算出的相邻变量的指针。以下是原始文章,其中volatile
用于模拟当前编译器单元无法访问的外部函数,例如虚拟函数调用,封闭源代码库函数等]
我想知道以下代码中的return t.a;
是否将优化为return 0;
。
//revision 1
struct T
{
int a;
int b;
};
void f_(int * p)
{
*p = 1;
}
auto volatile f = f_;
int main()
{
T t;
t.a = 0;
t.b = 0;
for (int i = 0; i < 20; ++i)
{
f(&t.b);
}
return t.a;
}
好吧it's not。相当公平,因为函数
f
中的代码可以使用offsetof
获取指向t
的指针,然后更改t.a
。因此,优化掉
t.a
的负载并不安全。[编辑:再三考虑,在这里
offsetof
是不够的。我们需要container_of
,似乎无法在标准C++中实现。]但是
offsetof
不能用于非标准布局类型。所以我尝试了以下代码://revision 2
#include <type_traits>
struct T
{
private:
char dummy = 0;
public:
int a;
int b;
};
static_assert(!std::is_standard_layout_v<T>);
void f_(int * p)
{
*p = 1;
}
auto volatile f = f_;
int main()
{
T t;
t.a = 0;
t.b = 0;
for (int i = 0; i < 20; ++i)
{
f(&t.b);
}
return t.a;
}
不幸的是,它仍然无法正常工作。我的问题是:
t.a
的负载是否安全(修订版2)T
设置为更特殊的类型,或者将b
中的成员T
设置为属性说明符)附言以下代码针对
return t.a;
进行了优化,但是循环产生的代码效率低下。而且,临时变量变戏法很麻烦。
//revision 3
struct T
{
int a;
int b;
};
void f_(int * p)
{
*p = 1;
}
auto volatile f = f_;
int main()
{
T t;
t.a = 0;
t.b = 0;
for (int i = 0; i < 20; ++i)
{
int b = t.b;
f(&b);
t.b = b;
}
return t.a;
}
最佳答案
因为没有对象offsetof
和T::a
可以从中获取T::b
,所以使用T::b
从T::a
中获取T::b
是非法的。在另一个方向上,从T::a
到达T
是pointer-interconvertible,因为后者可以与container_of
进行指针互转换。在注释中与Peter相反(尽管在Linux内核中存在possible宏),但&t.b - 1
不会产生指向t.a
的指针,因为T::b
和T::a
不可指针互换。
注意,给定指向T::a
的指针,您仍然需要使用 std::launder
来访问T::b
:
auto p = &t.a;
std::launder(reinterpret_cast<T*>(p))->b = 1;
因此,具有足够侵略性的编译器确实可以得出结论,给定f
的指针,没有替代的t.a
可以访问t.b
。但是,目前似乎没有主流编译器执行此优化。
关于c++ - C++:在成员变量的指针传递到其他地方的情况下,如何使编译器优化内存访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64621479/