c++ - Error de segmentación en boost:: multi_array
segmentation-fault boost-multi-array (1)
El siguiente código da un error de segmentación:
#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;
}
El archivo binario de entrada contiene datos unsigned int
y su tamaño según lo informado por ls -l
es 231 * (5 + 1) 4 = 5544 bytes. Intenté leer el archivo y almacenar los datos en un vector y encontré que el tamaño del vector es 231 (5 + 1) = 1386. El análisis del archivo core usando gdb da el siguiente resultado.
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
¿Alguna sugerencia? Gracias.
El problema es que la clase de iterador de entrada ii_t<>
de la respuesta de SO referida está "leyendo" demasiados elementos porque el istream
ajustado no devuelve EOF
hasta la desreferencia del iterador después del que devolvió el último elemento en el archivo. El elemento de datos devuelto está corrompiendo el bloque de memoria asignado en el objeto multi_array
.
Si cambia la clase ii_t<>
a la siguiente, debería obtener un mejor comportamiento:
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;
};
Los cambios relevantes están en la función bool operator!=(ii_t const& rhs)
, donde, si es necesario, se realiza una lectura ficticia (luego se deshace) en la istream
envuelta para determinar si la istream está en el EOF.
Tenga en cuenta que no estoy afirmando que esta es la mejor técnica para manejar la situación de EOF
, pero parece funcionar.