为什么要编译此代码

Why does this code compile

本文关键字:代码 编译 为什么      更新时间:2023-10-16

我不明白为什么这段代码编译并运行时没有错误:

#include <iostream>
#include <stdio.h>
#include <Eigen/Dense>
int
main (int argc, char *argv[])
{
  typedef Eigen::Matrix<double, 5, 3/*FIXME: Should be 5*/> Matrix5;
  Matrix5 test;
  test << 2,0,0,1,1, 
          0,2,0,1,1,
          0,0,2,2,2,
          0,1,2,0,0,
          0,1,1,0,0; // We filled a 5*5 matrix in a 5*3 matrix
  //std::cout << "Test matrix:n" << test << std::endl;
  return (0);
}

这就是我编译代码的方式:

g++ test_eigen.cpp -o test_eigen -I/usr/include/eigen3 -O3 -DEIGEN_NO_DEBUG

取消对std::cout的注释,编译,再次运行,就会出现分段错误。我在Ubuntu 14.04上使用的是Eigen 3.2.0-8。

如建议;以下是Valgrind的输出(已评论std::cout):

$ valgrind ./test_eigen
==12380== Memcheck, a memory error detector
==12380== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==12380== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==12380== Command: ./test_eigen
==12380== 
==12380== 
==12380== HEAP SUMMARY:
==12380==     in use at exit: 0 bytes in 0 blocks
==12380==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==12380== 
==12380== All heap blocks were freed -- no leaks are possible
==12380== 
==12380== For counts of detected and suppressed errors, rerun with: -v
==12380== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

写入超过数据结构的已分配边界(例如,将5x5矩阵写入已分配的5x3)是未定义的行为。它可能会崩溃,也可能不会崩溃,不幸的是,它甚至可能在没有可见错误的情况下运行到完成。

使用valgrind运行您的代码,以精确定位与您描述的类似的内存问题。

我怀疑您编译时禁用了断言。

Eigen重载operator<<,使矩阵能够用逗号分隔的列表进行初始化。(请注意,这也意味着隐藏在幕后的逗号运算符被覆盖)。逗号分隔的项目列表可以包含数字,但也可以包含向量或其他矩阵。

这种灵活性是有代价的。Eigen在编译时无法判断逗号分隔的列表包含的项目太多或太少。相反,它在运行时检测到项目过多/过少,并通过eigen_assert进行投诉。如果您在禁用断言的情况下进行编译,则会让过于完整的项列表从SIGABRT变为未定义的行为。

这种事情总是发生在未定义的行为中,添加一条语句可以神奇地让代码无错误地运行。不要把这看作是一切正常的迹象。它只是碰巧在运行。未定义的行为总是不好的。在某些情况下,它"有效"并不意味着下次使用该程序时,代码不会重新格式化硬盘。

为什么要编译此代码?

因为编译器将无法知道在编译时您传递给operator<<的元素数量。该信息仅在运行时可用。