errore "indirizzo non da malloc ()" utilizzando la recinzione elettrica
-
29-09-2020 - |
Domanda
Ho scritto un programma di test case per dimostrare un problema con un mio programma più ampio, e il caso di test ha un bug che il programma originale non ha.
Ecco il file di intestazione:
// compiled with g++ -I/usr/local/bin/boost_1_43_0 -Wall -std=c++0x -g test.cpp
#include <bitset>
#include <boost/shared_ptr.hpp>
#include <vector>
typedef std::vector< std::vector< std::bitset<11> > > FlagsVector;
namespace yarl
{
namespace path
{
class Pathfinder;
}
namespace level
{
class LevelMap
{
// Member Variables
private:
int width, height;
FlagsVector flags;
public:
boost::shared_ptr<path::Pathfinder> pathfinder;
// Member Functions
LevelMap(const int, const int);
int getWidth() const {return width;}
int getHeight() const {return height;}
bool getFifthBit(const int x, const int y) const
{
return flags.at(x).at(y).test(5);
}
};
class Level
{
// Member Variables
public:
LevelMap map;
// Member Functions
public:
Level(const int w=50, const int h=50);
};
}
namespace path
{
class Pathfinder
{
// Member Variables
private:
boost::shared_ptr<level::LevelMap> clientMap;
// Member Functions
public:
Pathfinder() {}
Pathfinder(level::LevelMap* cm)
: clientMap(cm) {}
void test() const;
};
}
}
ed ecco il file di implementazione:
#include <iostream>
#include "test.hpp"
using namespace std;
namespace yarl
{
namespace level
{
LevelMap::LevelMap(const int w, const int h)
: width(w), height(h), flags(w, vector< bitset<11> >(h, bitset<11>())),
pathfinder(new path::Pathfinder(this))
{}
Level::Level(const int w, const int h)
: map(w,h)
{
map.pathfinder->test();
}
}
namespace path
{
void Pathfinder::test() const
{
int width = clientMap->getWidth();
int height = clientMap->getHeight();
cerr << endl;
cerr << "clientMap->width: " << width << endl;
cerr << "clientMap->height: " << height << endl;
cerr << endl;
for(int x=0; x<width; ++x)
{
for(int y=0; y<height; ++y)
{
cerr << clientMap->getFifthBit(x,y);
}
cerr << "***" << endl; // marker for the end of a line in the output
}
}
}
}
int main()
{
yarl::level::Level l;
l.map.pathfinder->test();
}
Collego questo programma con la recinzione elettrica, e quando lo eseguo si interrompe con questo errore:
ElectricFence Aborting: free(bffff434): address not from malloc().
Program received signal SIGILL, Illegal instruction.
0x0012d422 in __kernel_vsyscall ()
il backtracing da gdb mostra che l'istruzione illegale si trova nel distruttore generato dal compilatore di Pathfinder
, che sta avendo problemi a distruggere il suo shared_ptr.Qualcuno ha capito perche'?
Soluzione
yarl::level::Level l;
.
Si istanziare una variabile automatica Level
, che, nel suo costruttore costruisce il suo membro pathfinder
come:
pathfinder(new path::Pathfinder(this))
.
Poi nel costruttore Pathfinder
, prende il puntatore Level
che si passa e assegna a un shared_ptr
. Il shared_ptr
prende quindi la proprietà di questo puntatore.
Questo non è corretto per diversi motivi:
- .
- Un
shared_ptr
deve essere utilizzato per gestire oggetti assegnati dinamicamente, non oggetti automaticamente assegnati - Se si desidera utilizzare
shared_ptr
, dovresti usarlo ovunque: come ora, passano i puntatori grezzi (ad esempio al costruttore diPathfinder
, ma quindi archiviali comeshared_ptr
s. Questo apre una grande lattina di vermi di proprietà. . - Il modo corretto per assegnare
this
a unshared_ptr
è quello di derivare daenable_shared_from_this
; Nota tuttavia che non è possibile ottenere unshared_ptr
dathis
in un costruttore.
Quando il shared_ptr
viene distrutto, proverà a eliminare il puntatore che riesce. In questo caso, tuttavia, quel puntatore non è a un oggetto assegnato dinamicamente (cioè, assegnato con new
), ma a un oggetto assegnato automaticamente (cioè, sullo stack). Quindi, l'errore.
Se non hai bisogno di qualcosa per prendere la proprietà di una risorsa, non c'è niente di sbagliato nell'utilizzo di un puntatore grezzo (o di un riferimento, se hai questa opzione).
Altri suggerimenti
Stai costruendo shared_ptr
da un puntatore che non dovrebbe essere gestito da shared_ptr.(Il puntatore this
)
Quando l'ultima copia di Shared_ptr viene distrutta questa memoria è libera - Quando infatti non dovrebbe - il this
è in serie in questo caso.
C'è una ragione per cui il costruttore di shared_ptr
è esplicito - è esattamente per evitare una conversione inosservato come una conversione inosservato dal puntatore regolare, che non deve essere gestito da shared_ptr, a uno shared_ptr - una volta che si passa un tale puntatore in Shared_ptr, il tuoIl programma è condannato: l'unica via d'uscita è eliminando il puntatore che non hai intenzione di eliminare.
In generale è consigliabile costruire il puntatore condiviso con nuovo direttamente - come ptr(new Somethings(x,y,z)
- in questo modo non rischi di un'eccezione che perde il tuo assegnato, ma non assegnato a una memoria shared_ptr.
Il Level
contiene un LevelMap
variabile membro.Quando il Level
viene distrutto, distruggerà anche il suo LevelMap
.
D'altra parte un puntatore a questo LevelMap
il membro è passato a Pathfinder
, che crea un shared_ptr<>
dal puntatore passato.Questo appena creato shared_ptr<>
pensa di possedere l'oggetto a cui punta e cercherà di distruggerlo una volta che il Pathfinder
viene distrutto.
Quindi il LevelMap
viene distrutto più volte.
Nell'esempio il LevelMap
viene creato in pila.Pertanto il delete
chiamato da shared_ptr<>
può vedere che l'indirizzo non è dall'heap e si ottiene un errore.Se anche il tuo vero programma ha questo problema, ma tutti quegli oggetti sono allocati dinamicamente, l'errore probabilmente non verrà rilevato.Si otterrà solo la corruzione della memoria silenziosa e strani arresti anomali in seguito.