【问题标题】:Weird Pointer issue in C++C ++中的奇怪指针问题
【发布时间】:2011-03-09 08:56:36
【问题描述】:

我遇到了一个非常令人沮丧的指针问题。我之前在这里发过: TOUGH: Dealing with deeply nested pointers in C++

但是那个帖子太长了而且过时了,所以我选择重新发布更多细节。

这是定义我的类型的头文件:

#include <string>
#include <vector>
#include <sstream>
#include <iostream>

#define USE_3D_GEOM
//#define USE_2D GEOM

#define DEBUG

#ifdef USE_3D_GEOM
 #define DIMENSIONS 3
#elif USE_2D_GEOM
 #define DIMENSIONS 2
#else
 #define DIMENSIONS 1
#endif

#ifndef _COMMON_H
#define _COMMON_H

template<class T>
inline T from_string(const std::string& s)
{
     std::istringstream stream (s);
     T t;
     stream >> t;
     return t;
};

template <class T>
inline std::string to_string (const T& t)
{
std::stringstream ss;
ss << t;
return ss.str();
}

enum e_ensemble_kind 
{
  MICROCANONICAL,
  CANONICAL,
  NVT,
  GRAND_CANONICAL,
  NPT,
  NVE
};

enum e_potential_kind 
{
  HARD_SPHERE,
  SQUARE_WELL,
  LENNARD_JONES
};

enum e_file_types
{
  MC_SIMPLE,
  NAMD,
  GROMACS,
  CHARMM
};

#ifdef USE_3D_GEOM
typedef struct s_coordinates t_coordinates;
#endif

#ifdef USE_2D_GEOM
typedef struct s_coordinates t_coordinates;
#endif

typedef struct s_particle t_particle;

typedef struct s_bond t_bond;

typedef struct s_angle t_angle;


typedef struct s_dihedral t_dihedral;

typedef struct s_molecule t_molecule;

typedef struct s_lj_param t_lj_param;

typedef struct s_bond_param t_bond_param;

typedef struct s_angle_param t_angle_param;

typedef struct s_dih_param t_dih_param;

typedef struct s_lookup_tab t_lookup_tab;

#ifdef USE_3D_GEOM
struct s_coordinates
{
  double x;
  double y;
  double z;

  s_coordinates& operator+(const s_coordinates &to_add)
  {
    x += to_add.x;
    y += to_add.y;
    z += to_add.z;
    return *this;
  }
  s_coordinates& operator-(const s_coordinates &to_subtract)
  {
    x -= to_subtract.x;
    y -= to_subtract.y;
    z -= to_subtract.z;
    return *this;
  }
  s_coordinates& operator=(const s_coordinates &to_assign)
  {
    x = to_assign.x;
    y = to_assign.y;
    z = to_assign.z;
    return *this;
  }
  bool operator==(const s_coordinates &to_assign)
  {

    return x == to_assign.x && y == to_assign.y && z == to_assign.z;
  }
};
#endif

#ifdef USE_2D_GEOM
struct s_coordinates
{
  double x;
  double y;

  s_coordinates& operator+(const s_coordinates &to_add)
  {
    x += to_add.x;
    y += to_add.y;
    return *this;
  }
  s_coordinates& operator-(const s_coordinates &to_subtract)
  {
    x -= to_subtract.x;
    y -= to_subtract.y;
    return *this;
  }
  s_coordinates& operator=(const s_coordinates &to_assign)
  {
    x = to_assign.x;
    y = to_assign.y;
    return *this;
  }
  bool operator==(const s_coordinates &to_assign)
  {

    return x == to_assign.x && y == to_assign.y;
  }
};
#endif

typedef struct s_particle
{
  t_coordinates position;
  double charge;
  double mass;
  std::string name;
  std::vector<t_lj_param>::iterator my_particle_kind_iter;

  s_particle& operator=(const s_particle &to_assign)
  {
    position = to_assign.position;
    charge = to_assign.charge;
    mass = to_assign.mass;
    name = to_assign.name;
    my_particle_kind_iter = to_assign.my_particle_kind_iter;
    return *this;
  }
} t_particle;

struct s_bond
{
  t_particle * particle_1;
  t_particle * particle_2;
  std::vector<t_bond_param>::iterator my_bond_kind_iter;

  s_bond& operator=(const s_bond &to_assign)
  {
    particle_1 = to_assign.particle_1;
    particle_2 = to_assign.particle_2;
    my_bond_kind_iter = to_assign.my_bond_kind_iter;
    return *this;
  }
};

struct s_angle
{
  t_particle * particle_1;
  t_particle * particle_2;
  t_particle * particle_3;
  std::vector<t_angle_param>::iterator my_angle_kind_iter;

  s_angle& operator=(const s_angle &to_assign)
  {
    particle_1 = to_assign.particle_1;
    particle_2 = to_assign.particle_2;
    particle_3 = to_assign.particle_3;
    my_angle_kind_iter = to_assign.my_angle_kind_iter;
    return *this;
  }
};


struct s_dihedral
{
  t_particle * particle_1;
  t_particle * particle_2;
  t_particle * particle_3;
  t_particle * particle_4;
  std::vector<t_dih_param>::iterator my_dih_kind_iter;

  s_dihedral& operator=(const s_dihedral &to_assign)
  {
    particle_1 = to_assign.particle_1;
    particle_2 = to_assign.particle_2;
    particle_3 = to_assign.particle_3;
    particle_4 = to_assign.particle_4;
    my_dih_kind_iter = to_assign.my_dih_kind_iter;
    return *this;
  }
};

struct s_molecule
{
  std::string res_name;
  std::vector<t_particle> my_particles;
  std::vector<t_bond> my_bonds;
  std::vector<t_angle> my_angles;
  std::vector<t_dihedral> my_dihedrals;

  s_molecule& operator=(const s_molecule &to_assign)
  {
    res_name = to_assign.res_name;
    my_particles = to_assign.my_particles;
    my_bonds = to_assign.my_bonds;
    my_angles = to_assign.my_angles;
    my_dihedrals = to_assign.my_dihedrals;
    return *this;
  }
};

struct s_lj_param
{
  double epsilon;
  double sigma;
  std::string atom_kind_name;
};

struct s_bond_param
{
  std::string atom_1;
  std::string atom_2;
  double bond_coeff;
  double default_length;
};

struct s_angle_param
{
  std::string atom_1;
  std::string atom_2; 
  std::string atom_3;
  double angle_coeff;
  double default_angle;
};

struct s_dih_param
{
  std::string atom_1;
  std::string atom_2; 
  std::string atom_3; 
  std::string atom_4;  
  std::vector<double> dih_coeff;
  std::vector<unsigned int> n;
  std::vector<double> delta;
};

struct s_lookup_tab {
  std::string name;
  int code;
};

#endif /*_COMMON_H*/

这是一个调用,我将一个类型为 t_molecule 的 var(参见上面关于 t_molecule 的定义的标题)添加到一个分子数组中。

    void Molecule_Manager_Main::add_molecule(const t_molecule new_molecule)
{
    std::cout << "TYPE :" << new_molecule.res_name << std::endl; 
    std::cout << "3: BOND PARTICLE 1 : "
      << new_molecule.my_bonds[new_molecule.my_bonds.size()-1].particle_1->name
          << std::endl;  
    std::cout << "3: BOND PARTICLE 2 : "
    << new_molecule.my_bonds[new_molecule.my_bonds.size()-1].particle_2->name
          << std::endl; 
    std::cout << "3: BOND ITER CONST : "
    << new_molecule.my_bonds[new_molecule.my_bonds.size()-1].my_bond_kind_iter->bond_coeff
          << " "
    << new_molecule.my_bonds[new_molecule.my_bonds.size()-1].my_bond_kind_iter->default_length
          << std::endl;
    my_molecules.push_back(new_molecule);
    std::cout << "99: INDEX : " << my_molecules.size()-1 << std::endl;
    std::cout << "TYPE :" << my_molecules[my_molecules.size()-1].res_name << std::endl; 
    std::cout << "4: BOND PARTICLE 1 : "
          << my_molecules[my_molecules.size()-1].my_bonds[my_molecules[my_molecules.size()-1].my_bonds.size()-1].particle_1->name
          << std::endl;  
    std::cout << "4: BOND PARTICLE 2 : "
    << my_molecules[my_molecules.size()-1].my_bonds[my_molecules[my_molecules.size()-1].my_bonds.size()-1].particle_2->name
          << std::endl; 
    std::cout << "4: BOND ITER CONST : "
    << my_molecules[my_molecules.size()-1].my_bonds[my_molecules[my_molecules.size()-1].my_bonds.size()-1].my_bond_kind_iter->bond_coeff
          << " "
    << my_molecules[my_molecules.size()-1].my_bonds[my_molecules[my_molecules.size()-1].my_bonds.size()-1].my_bond_kind_iter->default_length
          << std::endl;
  add_performed = true;
}

这很好用……打印 resname 字符串,打印债券向量中最后一个债券的信息。然后,一旦我添加了所有分子。我称之为:

t_molecule * Molecule_Manager_Main::get_molecule(unsigned int index)
{
    std::cout << "TYPE :" << my_molecules[index].res_name << std::endl; 
    std::cout << "5: BOND PARTICLE 1 : "
      << my_molecules[index].my_bonds[my_molecules[index].my_bonds.size()-1].particle_1->name
          << std::endl;  
    std::cout << "5: BOND PARTICLE 2 : "
    << my_molecules[index].my_bonds[my_molecules[index].my_bonds.size()-1].particle_2->name
          << std::endl; 
    std::cout << "5: BOND ITER CONST : "
    << my_molecules[index].my_bonds[my_molecules[index].my_bonds.size()-1].my_bond_kind_iter->bond_coeff
          << " "
    << my_molecules[index].my_bonds[my_molecules[index].my_bonds.size()-1].my_bond_kind_iter->default_length
          << std::endl;
  return &(my_molecules[index]);
} 

这会在绑定线上出现段错误。

我可以从我在添加步骤中打印的索引中看出我没有覆盖我推送到向量上的分子(大小正在增长)..

换句话说,似乎正在发生的事情是: 读取子向量(有效)-> 在父向量中添加更多项目-> 重新读取子向量(seg-faults)

这些函数是将分子变量添加到向量中的唯一方法,分子变量仅添加一次,并且在我当前的测试中不会在死后修改。

有什么想法吗???提前谢谢你!

【问题讨论】:

  • 学习使用调试器,检查用于访问数组的索引。
  • 如果您稍微简化一下代码,我会更愿意提供帮助。请去掉不相关的行。
  • +1 用于调试器建议。如果您使用调试器,通常一眼就能诊断出这类问题。如果你运气不好,可能需要 2 分钟。 (如果是别人的代码,再加一分钟。)
  • +1 to peter - 调试器真的会帮到你很多。
  • 为了快速而肮脏的解决方案,我打印了索引。在添加过程中,我得到第一个分子的 0 和 12,所以:
    my_molecules[0].my_bonds[12].particle_1-&gt;name

    这有效...对于段错误我得到完全相同的 0 和 12,所以: my_molecules[0].my_bonds[12].particle_1-&gt;name

    这再次证实了我的说法,它看起来好像没有接触矢量元素,添加额外的矢量元素以某种方式破坏了它......

标签: c++ pointers segmentation-fault indirection multiple-indirection


【解决方案1】:

刚刚读到您访问了一个名为 my_bond_kind_iter 的变量。在父向量中添加更多项目后,它将调整大小。这意味着(假设您没有 C++0x 右值感知容器)子向量也将被复制,从而使所有现有的指针和引用无效。因此,当您尝试访问这个现在完全无效的旧迭代器时,您好,分段错误。如果您在子向量中添加更多内容,这当然也会发生。

向量迭代器不安全,您不能保留它们并在以后访问它们,因为向量会调整大小,这意味着移动内存,而这发生在实现的一时兴起。

【讨论】:

  • 酷,终于有真正的答案了!当我向父向量添加更多项目时,如何解决这个问题?网上有没有讨论这个的教程? (这似乎是一个更复杂的问题......)切换我的 gcc 版本会有帮助吗?
  • @Jason:不,这是一个基本的算法问题。您需要按索引将位置存储在向量中,而不是指针、引用或迭代器。或者,您可以使用不会使引用/指针无效的非调整大小容器,例如链表、映射或哈希映射。
  • 哦,还有一件事......只是为了澄清一下,键迭代器是键参数的向量(t_bond_param),在添加单个分子之前这些参数都已经到位......如果父向量发生变化,迭代器仍然无效,即使指向的子向量保持不变?
  • 如果这是问题所在,那么它是由于向量被定义为工作的方式。当大小超过容量时,元素被移动到更大的缓冲区中,从而改变它们的地址。通常的解决方案是使用没有这种行为的不同容器(例如,列表),将指针存储在向量中,或者提前计算出大小并将向量调整(或保留)到正确的大小然后注意不要增加大小。
  • 粒子指针也存在段错误...这是同一种问题吗?因为它们存储在子向量中?
【解决方案2】:

在不同的对象中存储迭代器以访问某些向量中的元素。当底层向量被修改时,这些迭代器会失效,例如通过添加新元素。取消引用这样的迭代器是未定义的行为。

当您添加新分子时,您可能会修改已存储迭代器的向量,并且稍后使用这些迭代器会导致分段错误。

【讨论】:

  • 是的,这就是问题所在!根据上面 DeadMG 的建议,我已经切换到索引,我的程序现在运行起来就像一个魅力!万岁!
【解决方案3】:

我会是你,我会大量重构这段代码。

大多数时候,当我遇到像你这样的问题时(而且这种情况变得非常罕见),我会重构代码,直到我清楚地看到问题为止。在这里,显然可以避免太多的重复。使用 typedefs、references、consts 来避免重复。

重构允许你重新组织你的代码和你的想法,简化,让问题变得明显。花点时间去做这件事,你就会找到问题的根源。

这可能不在这段代码中。

(关于重构,我推荐阅读这个:http://sourcemaking.com/refactoring

【讨论】:

  • 我在时间允许的情况下正在阅读您发送的指南。谢谢!顺便说一句,问题解决了——当向量调整大小时,向量结构中的指针变得无效......
  • 一个常见的错误,我也经历过这个地狱^^;
【解决方案4】:

我建议在编写跟踪代码时避免使用下标运算符(例如 my_molecules[index])(但不限于跟踪代码),更喜欢 at() 成员函数。

【讨论】:

  • 它是如何工作的?我查看了我的 O'Reily Pocket Reference C++ 并没有看到“at”函数部分......显然在 Google 中输入“at function”“at c”等是一个失败的冒险......你有一个好的这方面的资源?提前致谢!
猜你喜欢
  • 2015-07-17
  • 2013-02-04
  • 1970-01-01
  • 2020-11-28
  • 2012-05-29
  • 1970-01-01
  • 2014-09-11
  • 2016-01-31
  • 2023-04-07
相关资源
最近更新 更多