考虑以下代码(使用 Eigen ):
#include <Eigen/Dense>
#include <iostream>
template<int rows, int cols, int row, class R, class Rv, int N, class... Rs>
inline typename std::enable_if<sizeof...(Rs)==0>::type
setRow(Eigen::Matrix<R,rows,cols>&)
{}
template<int rows, int cols, int row, class R, class Rv, int N=0, class... Rs>
inline typename std::enable_if<sizeof...(Rs)==cols-N-1>::type
setRow(Eigen::Matrix<R,rows,cols>& m, Rv val, Rs...args)
{
m(row,N)=val;
setRow<rows,cols,row,R,Rv,N+1>(m,args...);
}
template<class T, int R, int C, int CUR_ROW>
class MatrixConstructor
{
Eigen::Matrix<T,R,C> m;
public:
MatrixConstructor(const Eigen::Matrix<T,R,C>& m)
: m(m)
{}
MatrixConstructor()
{}
template<class...Ts>
typename std::enable_if<sizeof...(Ts)==C && CUR_ROW<R-1,
MatrixConstructor<T,R,C,CUR_ROW+1>>::type operator()(Ts... vals)
{
setRow<R,C,CUR_ROW>(m,vals...);
return MatrixConstructor<T,R,C,CUR_ROW+1>(m);
}
template<class...Ts>
typename std::enable_if<sizeof...(Ts)==C && CUR_ROW==R-1,
Eigen::Matrix<T,R,C>>::type operator()(Ts... vals)
{
setRow<R,C,CUR_ROW>(m,vals...);
return m;
}
};
void test()
{
Eigen::Matrix<double,4,3> m=MatrixConstructor<double,4,3,0>()(1,2,3)
(4,5,6)
(7,8,9)
(5,4,3);
std::cout << m;
}
int main()
{ test(); }
我使用 gcc-4.8 编译它,并进行了全面优化和生成汇编列表的选项。这是我使用的命令:
g++ minitest.cpp -I/usr/include/eigen3 -std=c++0x -O3 -march=native -S -masm=intel
(我的 CPU 是运行 64 位 Linux 系统的 Intel(R) Xeon(R) CPU E3-1226 v3 — 希望现在 -march=native
对读者有意义。)
让我奇怪的是,用这个命令行生成的一些指令似乎是无意义的,甚至是多余的。参见例如test()
函数如何在设置堆栈后启动(有关 test()
和 main()
的完整代码,请参阅 here ) :
vmovsd xmm4, QWORD PTR .LC6[rip] # 1.0
lea rsi, [rsp+96]
vmovsd xmm5, QWORD PTR .LC7[rip] # 2.0
vmovsd QWORD PTR [rsp], xmm4
vmovapd xmm3, XMMWORD PTR [rsp+16] # What does it read?!
vmovapd xmm1, XMMWORD PTR [rsp] # And this!
vmovsd QWORD PTR [rsp+32], xmm5
vmovsd xmm0, QWORD PTR .LC8[rip] # 3.0
vmovapd XMMWORD PTR [rsp+304], xmm3 # And now even save this junk?!
vmovapd XMMWORD PTR [rsp+192], xmm1
vmovapd xmm3, XMMWORD PTR [rsp+48]
vmovapd xmm1, XMMWORD PTR [rsp+32]
vmovsd QWORD PTR [rsp+64], xmm0
vmovsd xmm7, QWORD PTR .LC12[rip] # 7.0
vmovapd XMMWORD PTR [rsp+336], xmm3
vmovapd XMMWORD PTR [rsp+224], xmm1
vmovapd xmm3, XMMWORD PTR [rsp+80]
vmovsd QWORD PTR [rsp+304], xmm7 # Even stranger — overwrites the junk
我已经在调试器中逐步读取了这些垃圾数据,并确认在它们之后 xmm3
和 xmm1
寄存器确实具有无意义的值。看着这个未定义值的读取,我开始怀疑我的程序确实试图访问一些应该无法访问的内存。但为什么?我在某处引入了 UB 吗?
我也试过运行用 -fsanitize=address
编译的程序,但它没有任何崩溃。
最佳答案
您的代码执行以下步骤:
- 使用未初始化的 Eigen::Matrix 成员创建未初始化的 MatrixConstructor 对象
- 设置一行 Eigen::Matrix 成员
- 创建一个新的 MatrixConstructor 对象,其 Eigen::Matrix 成员使用旧 MatrixConstruction 对象的 Eigen::Matrix 成员初始化
因此,在第 3 步中,您将复制仅设置了第一行的 Eigen::Matrix 对象。对象的其余值仍未初始化。由于这些都是临时对象,它们都分配在堆栈中,因此您看到从堆栈中读取垃圾也就不足为奇了。
请注意,这假定 Eigen::Matrix() 构造函数未初始化对象,从快速查看源代码来看,默认情况下它似乎没有这样做。
关于c++ - 为什么 GCC 生成的代码会从堆栈中读取垃圾?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31073782/