由于无效写入导致的分段错误

Posted

技术标签:

【中文标题】由于无效写入导致的分段错误【英文标题】:Segmentation fault due to invalid writes 【发布时间】:2020-04-28 20:09:48 【问题描述】:

我正在尝试创建一个迷宫类,它可以读取具有迷宫描述的输入流并返回一个迷宫。但是,当我使用这个给定的输入流运行测试时:

20 10
####################
#................<.#
#..................#
#...###............#
#.....#............#
#.....#............#
#...###............#
#..................#
#..................#
####################

它给出了一个分段错误,我在 valgrind 上运行目标文件来检查发生了什么:

Invalid write of size 8
==2482545==    at 0x4032CD: Maze::setTile(Position const&, Tile*) (maze.cpp:47)
==2482545==    by 0x40347B: Maze::read(std::istream&) (maze.cpp:67)
.....
==2482545==  Address 0x0 is not stack'd, malloc'd or (recently) free'd

我真的不明白为什么会出现分段错误或无效写入,在我的代码中,我应该在 setTile 函数中为每个图块分配空间,所以应该有空间供我编写。我还将 tile_collection 与我的构造函数堆叠在一起,因此当我调用 Maze(20,10) 时应该初始化 tile_collection,并且 setTile 内部的调整大小应该可以正常工作。你能指出我错过了什么吗?提前谢谢你。

Maze::Maze(int width,int height):
  Width(width),Height(height)
  vector<Tile*>* tile_collection=new vector<Tile*>;



void Maze::setTile(const Position &pos,Tile *tile)
  tile_collection.resize(pos.getX()+pos.getY()*Width);
  tile_collection[pos.getX()+pos.getY()*(Width)]=tile;



Maze *Maze::read(std::istream &in)
  int x;int y;char c;
  if ((in>>x)&&(in>>y))
      Maze *new_maze=new Maze(x,y);
      //loop over the specified maze dimension
        for (int i=0;i<y;i++)
          for (int j=0;j<x;j++)
            if (in>>c)
              //using tilefactory to change character into a tile
              TileFactory *fac=fac->getInstance();
              Tile* temp=fac->createFromChar(c);
              //if createFromChar fails, return nullptr, otherwise set tile at position j,i
              if (temp==nullptr)
                return nullptr;
              
              else
                new_maze->setTile(Position(j,i),temp);
              
            
          
        
        return new_maze;
  
  else
    return nullptr;
  



【问题讨论】:

vector&lt;Tile*&gt;* tile_collection=new vector&lt;Tile*&gt;; 正在分配给局部变量,而不是成员变量。您可以将tile_collection=new vector&lt;Tile*&gt;; 分配给成员,但最好的办法是消除指针并将vector&lt;Tile*&gt; tile_collection 定义为成员变量。保存指针和动态分配以备不时之需,而对于像std::vector 这样的库容器,这几乎是不可能的。他们的工作是为您处理内存管理。 更深入地看,tile_collection.resize(pos.getX()+pos.getY()*Width);. 符号的使用表明成员 tile_collection 不是指针,因此问题与我上面描述的不同。上面的内容仍然是错误的,这是内存泄漏,但可能与您正在寻找的错误无关。 感谢您的帮助!嗯,所以我只需要在我的头文件中定义 vector tile_collection 而不必初始化它?在我的 getTile 中调整大小的所有操作仍然有效吗? vector 照顾自己。如果您有足够的信息在构造函数中调整它的大小,并且使用widthheight,您可以Maze::Maze(int width,int height): Width(width),Height(height), tile_collection(width * height) 并省去自己以后必须resize 它。 【参考方案1】:

vector&lt;Tile*&gt;* tile_collection=new vector&lt;Tile*&gt;; 将初始化新的 local 变量并泄漏内存。它与成员tile_collection 完全无关。如果tile_collectionvector&lt;Tile*&gt; 类型的成员变量(即不是指针),它将由构造函数初始化而无需任何显式代码。

其次

tile_collection.resize(pos.getX()+pos.getY()*Width);
tile_collection[pos.getX()+pos.getY()*(Width)]=tile;

导致越界访问。对于大小为n 的向量,有效索引为0...n-1。 似乎也存在逻辑错误。 每次你都会调整向量的大小,向它写一些东西(你也会减小它的大小,例如当pos 接近(0, 0) 时)。也许你想要更多这样的东西:

除非迷宫不能动态增长,否则你只需要调整一次向量的大小,或者在大小正确的情况下进行初始化:

Maze::Maze(int width, int height):
  Width(width),
  Height(height),
  tile_collection(width * height)  

并稍微简化setTile

void Maze::setTile(const Position &pos,Tile *tile)
    tile_collection[pos.getX() + pos.getY() * Width] = tile;

【讨论】:

谢谢!所以我将只调整向量的大小,然后使用 back 方法在我的 setTile 函数中添加元素 如果你想使用push_back,你需要reserve(),而不是resize()。我稍微编辑了一下,也许这是你想要的更多。 您已经通过不断调用resize 清除了错误,但最好在构造函数中使用正确的大小初始化vector 并完全删除resize。多次调整大小可能比可能浪费一些未使用的指针更糟糕。 你可以调整高度,但调整宽度需要更多的工作。 @M.Chen 最后一个警告:成员按照它们在类中定义的顺序进行初始化。 Churil 通过使用参数解决了这个问题,但是如果你在 WidthHeight 之前定义了 Maze::Maze(int width, int height): Width(width), Height(height), tile_collection(Width * Height) tile_collection tile_collection 将使用 WidthHeightWidth 和 @ 之前进行初始化987654347@ 被初始化。一些编译器会警告你。其他的……嗯,调试愉快。【参考方案2】:
tile_collection.resize(pos.getX()+pos.getY()*Width);
tile_collection[pos.getX()+pos.getY()*(Width)]=tile;

一样
int p = pos.getX()+pos.getY()*Width;

tile_collection.resize(p);
tile_collection[p]=tile;

除了,现在很明显您正在访问越界。如果要写入位置p,则至少需要分配p + 1 元素,因为在C++ 中几乎所有内容都是从0 开始的。

【讨论】:

以上是关于由于无效写入导致的分段错误的主要内容,如果未能解决你的问题,请参考以下文章

由于 C 中的内存不足导致的分段错误

C - 将结构写入二维数组会导致分段错误

由于信号导致命令失败:分段错误:11 Xcode 8.0

Swift 编译器错误,由于信号导致命令失败:分段错误:11

全局指针导致分段错误?

在 Swift 3 中存档项目并获得“由于信号分段错误 11 导致命令失败”