【问题标题】:Segmentation fault on boost::multi_arrayboost::multi_array 上的分段错误
【发布时间】:2011-11-04 03:01:48
【问题描述】:

以下代码给出了分段错误:

#include <iostream>
#include <fstream>
#include "binItr.h"
#include <boost/multi_array.hpp>

using namespace std;

int main(){
   const char * xifile = "results/feretxiG1155V0P5T231K10.bin";

   const uint pSize = 5;
   const uint T = 231;

   ifstream xiFileId(xifile, ios::binary);

   typedef boost::multi_array<uint, 2> array_type;
   array_type xi(boost::extents[T][pSize + 1]);

   //the ii_t class in the following line is taken from http://stackoverflow.com/questions/1855704/c-binary-file-i-o-to-from-containers-other-than-char-using-stl-algorithms written by http://stackoverflow.com/users/14065/loki-astari

   ii_t<uint> xi_in(xiFileId);

   copy(xi_in, ii_t<uint>(), xi.data());
   return 0;
}

输入二进制文件包含unsigned int 数据,ls -l 报告的数据大小为 231*(5+1)4 = 5544 字节。我尝试读取文件并将数据存储在向量中,发现向量大小为 231(5+1) = 1386。使用 gdb 分析核心文件得到以下输出。

    Program terminated with signal 6, Aborted.

    #0  0x00007fb71130ea75 in raise (sig=<value optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64   ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
   in ../nptl/sysdeps/unix/sysv/linux/raise.c

    (gdb) bt
    #0  0x00007fb71130ea75 in raise (sig=<value optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
    #1  0x00007fb7113125c0 in abort () at abort.c:92
    #2  0x00007fb7113484fb in __libc_message (do_abort=<value optimized out>, fmt=<value optimized out>) at ../sysdeps/unix/sysv/linux/libc_fatal.c:189
    #3  0x00007fb7113525b6 in malloc_printerr (action=3, str=0x7fb711425cd8 "double free or corruption (!prev)", ptr=<value optimized out>) at malloc.c:6266
    #4  0x00007fb711358e83 in __libc_free (mem=<value optimized out>) at malloc.c:3738
    #5  0x00000000004018c4 in __gnu_cxx::new_allocator<unsigned int>::deallocate (this=0x7fffc618d2f8, __p=0x2295290) at /usr/include/c++/4.4/ext/new_allocator.h:95
    #6  0x000000000040152f in boost::multi_array<unsigned int, 2ul, std::allocator<unsigned int> >::deallocate_space (this=0x7fffc618d290) at /usr/include/boost/multi_array.hpp:484
    #7  0x0000000000401077 in boost::multi_array<unsigned int, 2ul, std::allocator<unsigned int> >::~multi_array (this=0x7fffc618d290, __in_chrg=<value optimized out>) at /usr/include/boost/multi_array.hpp:468
    #8  0x0000000000400d4e in main () at segTest.cpp:30

有什么建议吗? 谢谢。

【问题讨论】:

  • 就我所见,似乎没有什么问题,但你能提供更多关于 ii_t 的信息吗?另外,您是否尝试过通过 valgrind 运行它?
  • 我试过 valgrind 并没有提到任何内存泄漏。 ii_t 来自stackoverflow.com/questions/1855704/…。最后我通过不使用 ii_t 解决了这个问题 :)
  • 如果注释掉 copy() 行,它仍然是段错误吗?
  • @fileoffset 是的,你是对的。它来自副本 - 但由于 ii_t 类。当我删除它时-段错误消失了-尽管我花了很多时间:)
  • 好的。迈克尔似乎已经解决了这个问题。 但是这个 ii_t 类不是为读取像boost::multi_array&lt;uint, 2&gt; 这样的复杂类而设计的。复杂类有构造函数,这个迭代器使用 read() 覆盖对象。这会破坏原始内容。此类仅设计用于 POD 类型(即任何没有构造函数并且如果结构没有其成员具有构造函数)。

标签: c++ segmentation-fault boost-multi-array


【解决方案1】:

问题是来自referred SO answer 的ii_t&lt;&gt; 输入迭代器类正在“读取”一个太多的项目,因为包装的istream 直到迭代器的取消引用才会返回EOF 在返回文件中最后一项的那个之后。额外返回的数据项正在破坏multi_array 对象中分配的内存块。

如果您将ii_t&lt;&gt; 类更改为以下内容,您应该会获得更好的行为:

template<typename T>
struct ii_t: public iterator<input_iterator_tag, void, void, void, void>
{
  ii_t(std::istream& str)
    :m_str(&str)
  {}
  ii_t()
    :m_str(NULL)
  {}
  ii_t& operator++()   {return *this;}  // increment does nothing.
  ii_t& operator++(int){return *this;}
  T& operator*()
  {
    // On the de-reference we actuall read the data into a local //// static ////
    // Thus we can return a reference
    static T result;
    m_str->read(reinterpret_cast<char*>(&result),sizeof(T));
    return result;
  }
  // If either iterator has a NULL pointer then it is the end() of stream iterator.
  // Input iterators are only equal if they have read past the end of stream.
  bool operator!=(ii_t const& rhs)
  {
      // we need to make sure we're not just about to hit EOF
      // if we already haven't
      if (m_str && m_str->good()) {
        char dummy;
        m_str->read(&dummy,1);
        if (m_str->good()) {
            m_str->putback(dummy);
        }
      }

      if (rhs.m_str && rhs.m_str->good()) {
        char dummy;
        rhs.m_str->read(&dummy,1);
        if (rhs.m_str->good()) {
            rhs.m_str->putback(dummy);
        }
      }

      bool lhsPastEnd  = (m_str == NULL)     || (!m_str->good());
      bool rhsPastEnd  = (rhs.m_str == NULL) || (!rhs.m_str->good());

      return !(lhsPastEnd && rhsPastEnd);
  } 

  private:
    std::istream*   m_str;
};

相关更改在 bool operator!=(ii_t const&amp; rhs) 函数中,如有必要,将在包装的 istream 上执行(然后撤消)虚拟读取以确定 istream 是否位于 EOF。

请注意,我并没有声称这是处理 EOF 情况的最佳技术,但它似乎有效。

【讨论】:

  • 非常感谢。已验证分段违规现在不存在。
  • 好的。我看到了问题。 std::copy 将在 ++ 操作后检查流的状态。但是因为这无济于事,所以它永远都是真的。因此,当您取消引用它(并且它失败)时,它无法知道它出错了,因此使用的对象现在是最后一个对象的副本。
  • 在这种情况下,我们现在有两个使用相同内部缓冲区的数组对象。这会使用户代码面临双重删除以及与共享缓冲区相关的其他问题。因此,如上所述,关于 OP 问题 ii_t 并非旨在与非 POD 类型一起使用。 (注意:这不会改变 Michael 已修复 POD 类型代码的事实)。
  • @Loki:我不认为问题是用户错误 - 这是从文件中读取最后一项后,尚未设置 EOF,因此迭代器不比较相等到“空”迭代器。这表明另一个取消引用是可以的 - 但是对该取消引用的读取将返回 EOF 而不是数据(因此 static T result 保持不变)。我不认为我添加到 ii_t 的内容是处理此问题的最佳方法,但如果不完全更改 ii_t 似乎最简单。
  • @Michael Burr:你是对的。我只是需要一些时间来分析使用情况(并删除了该评论)。你的修复很好。 ;-(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-29
  • 1970-01-01
  • 1970-01-01
  • 2013-12-03
相关资源
最近更新 更多