Perché il mio codice C ++ causando un errore di segmentazione e dopo aver utilizzato la funzione di lettura (...)?

StackOverflow https://stackoverflow.com/questions/677444

Domanda

La mia domanda sta sospendendo su una riga di codice che sembra avere niente di sbagliato con esso, tuttavia il mio IDE sembra essere sospensione su quella linea con l'errore:

  

gdb / mi (24/03/09 13:36) (. Uscito segnale 'SIGSEGV' ricevuto Descrizione:. Segmentation fault.)

La riga di codice chiama semplicemente un metodo che non ha codice in esso. Non è un errore di segmentazione quando si ha un riferimento null? In caso affermativo, come può un metodo vuoto avere riferimento null?

Questo pezzo di codice, sembra essere la causa del problema:

#include <sys/socket.h>

#define BUFFER_SIZE 256

char *buffer;

buffer = (char*)GetSomePointer()->SomeStackMemoryString.c_str();
int writeResult = write(socketFD, buffer, BUFFER_SIZE);

bzero(buffer, BUFFER_SIZE);
int readResult = read(socketFD, buffer, BUFFER_SIZE);

Quando la linea con il metodo read(...) è commentata, il problema va via.

Aggiornamento:

Ho cambiato la domanda a puntare verso il problema reale, e ho rimosso tutto il codice irrilevante - e ho anche risposto alla mia domanda proprio modo che la gente leggendo questo sa precisamente qual è il problema, si prega di leggere la mia risposta prima di dire "sei un idiota!".

È stato utile?

Soluzione

Il tuo codice è falso: i punti di buffer a qualche pezzo casuale di memoria. Io non sono sicuro perché la linea con Bzero non sta venendo a mancare.

Il codice corretto è:

   char buffer[BUFFER_SIZE];

   bzero(buffer, BUFFER_SIZE);
   int readResult = read(socketFD, buffer, BUFFER_SIZE);

oppure è possibile utilizzare calloc (1, BUFFER_SIZE) per ottenere un po 'di memoria allocata (e azzerato).

Altri suggerimenti

In primo luogo, chiamando un metodo tramite un puntatore nullo o di riferimento è in senso stretto un comportamento indefinito. Ma può avere successo a meno che la chiamata è virtuale.

Chiamare metodi virtuali virtualmente (tramite un puntatore / riferimento, non dalla classe derivata con Classe :: metodo () modo di invocazione) sempre non riesce se il riferimento / puntatore è nullo perché le chiamate virtuali richiedono l'accesso a vtable e l'accesso al vtable tramite un puntatore nullo / riferimento è impossibile. Così non si può chiamare un metodo virtuale vuoto attraverso un riferimento / puntatore.

Per capire questo è necessario sapere di più su come è organizzato il codice. Per ogni metodo non inline c'è una sezione di segmento di codice che contiene il codice macchina attuare il procedimento.

Quando una chiamata viene eseguita non virtualmente (sia da una classe derivata o un metodo non virtuale attraverso un riferimento / puntatore) il compilatore sa esattamente quale metodo chiamare (polimorfismo). Quindi è solo inserisce una chiamata ad una porzione esatta di codice e passa questo puntatore come primo parametro lì. In caso di chiamare tramite puntatore nullo questo sarà nullo anche, ma non ti importa se il metodo è vuoto.

Quando una chiamata è fatto praticamente (attraverso un riferimento / puntatore) il compilatore non sa quale esattamente metodo da chiamare, si sa solo che c'è un tavolo di metodi virtuali e l'indirizzo della tabella viene memorizzato nell'oggetto. Al fine di trovare quello che metodo da chiamare è necessario prima dereference il puntatore / riferimento, arrivare al tavolo, ottiene l'indirizzo del metodo da essa e solo allora chiamare il metodo. Leggendo la tabella è fatto in fase di esecuzione, non durante la compilazione. Se il puntatore / riferimento è nullo si ottiene errore di segmentazione a questo punto.

Questo spiega anche il motivo per cui le chiamate virtuali non possono essere inline. Il compilatore semplicemente non ha idea di quale codice inline quando è guardando il sorgente durante la compilazione.

Senza codice, il meglio che posso fare è un ipotesi. Ma ecco qui:

Il "codice di lunga durata" sta scrivendo ad un puntatore non valido. (O un puntatore totalmente casuale, o andare oltre l'inizio / inizio di un tampone o array). Questo sembra essere clobbering tabella di funzione virtuale per l'oggetto -. O è sovrascrivendo il puntatore all'oggetto, o il membro vptr dell'oggetto, o è di sovrascrivere l'attuale tabella di funzione virtuale globale per quella classe

Alcune cose da provare:

  • Mettere un membro sentinella nella tua classe. Per esempio. un int che viene inizializzato a un modello noto (0xDEADBEEF o 0xcafebabe sono comuni) nel costruttore, e non è mai cambiato. Prima di effettuare la chiamata di funzione virtuale, verificare (assert ()) che ha ancora il giusto valore.
  • Provare a utilizzare un debugger di memoria. Su Linux, le opzioni includono Electric Fence (efence) o Valgrind.
  • eseguire il programma in un debugger (gdb va bene) e curiosare per vedere cosa c'è di sbagliato - o post mortem dopo la segfault accade, oppure impostando un punto di interruzione poco prima del posto sta andando a segfault
  • .

Non riesco a pensare a nessun motivo per cui un metodo vuoto da solo causerebbe un tale problema. Senza alcun altro contesto, la mia prima però sarebbe che un problema altrove sta corrompendo la vostra memoria e si dà il caso di manifestarsi in questo modo qui.

Abbiamo avuto quel tipo di un problema prima, e ho scritto su di esso in questa risposta qui . Quella stessa domanda ha un sacco di altri buoni consigli in esso troppo che potrebbe aiutare.

  

Non è un errore di segmentazione quando si ha un riferimento null?

Forse, ma non necessariamente. Quali sono le cause di un segfault è in qualche modo specifico per la piattaforma, ma in pratica significa che il programma sta accedendo memoria che non dovrebbe essere. Si potrebbe desiderare di leggere il wikipedia articolo per avere una migliore idea di quello che è.

Una cosa che si potrebbe verificare in poi, fa il metodo vuoto ha un tipo di ritorno? Potrei sbagliarmi su questo, ma se si restituisce un oggetto, ho potuto vedere come un costruttore di copia può ottenere invitato spazzatura se il metodo non è in realtà restituisce un oggetto. Ciò potrebbe causare ogni sorta di comportamento wonky.

Si ottiene lo stesso risultato se si cambia il suo tipo di ritorno di annullare o restituire un valore?

Il problema è che il buffer variabile è in uso la memoria non assegnata, che provoca il danneggiamento della memoria quando la funzione read(...) inserisce i dati in <=>.

normalmente, Bzero sarebbe effettivamente causare l'errore di segmentazione, ma perché una stringa viene assegnato alla locazione di memoria, la funzione di lettura è stato permesso di scrivere oltre la memoria allocata (causando la perdita).

/* this causes *some* memory to be allocated, 
 * tricking bzero(...) to not SIGSEGV */
buffer = (char*)GetSomePointer()->SomeStackMemoryString.c_str();

int writeResult = write(socketFD, buffer, BUFFER_SIZE);

Questa modifica risolve la perdita di memoria:

#define BUFFER_SIZE 256

// Use memory on the stack, for auto allocation and release.
char buffer[BUFFER_SIZE];

// Don't write to the buffer, just pass in the chars on their own.
string writeString = GetSomePointer()->SomeStackMemoryString;
int writeResult = write(socketFD, writeString.c_str(), writeString.length());

// It's now safe to use the buffer, as stack memory is used.
bzero(buffer, BUFFER_SIZE);
int readResult = read(socketFD, buffer, BUFFER_SIZE);

Stai chiamando il metodo virtuale dal costruttore di una classe di base? Questo potrebbe essere il problema: se si sta chiamando un metodo virtuale puro dalla classe Base nel costruttore Derived s ', ed è solo in realtà definito in classe <=>, si potrebbe finire per l'accesso a un record che ha vtable non è stato ancora fissato, in quanto costruttore <=> s 'non è stato eseguito in quel punto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top