View Full Version : [C++] double free alla prima free()
Barbalbero
03-04-2011, 23:39
Ho una struttura
struct Struttura{
tipo **matrice;
};
più avanti nel codice utilizzo la struttura:
Struttura * var = (Struttura *) malloc(sizeof(Struttura));
Struttura->matrice = (tipo **) malloc(dimensione * sizeof (tipo *));
for (int i = 0; i < altra_dimensione; i++) {
Struttura->matrice[i] = (tipo *) malloc(altra_dimensione * sizeof (tipo));
}
;
più avanti ancora decido di deallocare tutto:
for(int i=0;i<dimensione;i++){
free(Struttura->matrice[i]);
}
...
Alla prima occasione in cui chiamo la free() ottengo un errore:
double free or corruption (out): 0x08ccb060
Ho guardato ovunque, non ho trovato una soluzione che funzioni.
C'è qualche errore in ciò che faccio?
Se non chiamo la free il programma funziona correttamente, e anzi vi dirò di più... se al posto del ciclo per deallocare la matrice ci metto un doppio ciclo per stampare i valori della matrice, me li stampa senza problemi. Questo significa che la memoria non è stata già deallocata e non è nemmeno corrotta.
(Nota: Se non dealloco la matrice ma dealloco la Struttura, funziona, ma suppongo che resti della memoria spazzatura, per questo insisto per deallocare la matrice. Ho notato che se ciclo l'esecuzione del programma infinite volte, la memoria allocata continua a crescere, quindi suppongo che sia a causa di questa mancata deallocazione)
(per semplificare il codice non l'ho scritto, ma in realtà la struct Struttura è puntata da un puntatore all'interno di un'altra struttura, ma non credo che la cosa sia rilevante)
tomminno
04-04-2011, 07:42
Se stai effettivamente usando C++ non usare assolutamente malloc e free.
Per quanto riguarda il tuo errore mi sembra che il problema possa essere l'indice su cui iteri:
malloc: for (int i = 0; i < altra_dimensione; i++)
free: for(int i=0;i<dimensione;i++)
Oppure potrebbe essere qualcos'altro che non hai postato.
Barbalbero
04-04-2011, 08:41
il bound del ciclo forse l'ho copiato sbagliato, ma se il problema fosse quello il problema salterebbe fuori solo ad una certa iterazione, non alla prima, come accade adesso.
Le altre cose che faccio nel programma non credo siano rilevanti, poiche si tratta solo di operazioni sui dati, nessun'altra free. Ora non posso, ma più tardi proverò a deallocare immediatamente dopo l'allocazione, per esserne sicuro.
Perchè non devo usare malloc? advanced linux programming consiglia di usare malloc
il bound del ciclo forse l'ho copiato sbagliato, ma se il problema fosse quello il problema salterebbe fuori solo ad una certa iterazione, non alla prima, come accade adesso.
Le altre cose che faccio nel programma non credo siano rilevanti, poiche si tratta solo di operazioni sui dati, nessun'altra free. Ora non posso, ma più tardi proverò a deallocare immediatamente dopo l'allocazione, per esserne sicuro.
Perchè non devo usare malloc? advanced linux programming consiglia di usare malloc
scrivi in C++, usa le caratteristiche del C++; new e delete in vece rispettivamente di malloc (o calloc) e free, e non mischiare assolutamente le cose.
banryu79
04-04-2011, 10:40
@Barbalbero: potresti usare Valgrind per scoprire il problema.
Barbalbero
04-04-2011, 10:55
ok ho provato un nuovo codice:
struttura->tab->nrows = dim;
struttura->tab->ncols = 2 * dim + 3;
struttura->tab->matrix = (mpf_t **) malloc(struttura->tab->nrows * sizeof (mpf_t *));
for (int i = 0; i < struttura->tab->ncols; i++) {
struttura->tab->matrix[i] = (mpf_t *) malloc(struttura->tab->ncols * sizeof (mpf_t));
}
for (int i = 0; i < struttura->tab->ncols; i++) {
free(struttura->tab->matrix[i]);
}
tab è una struct all'interno della struct struttura
In tab c'è un array bidimensionale matrix con le relative dimensioni.
in pratica ho provato a deallocare subito dopo aver allocato lo spazio, per vedere se era colpa di qualche operazione intermedia.
Il problema persiste.
scrivi in C++, usa le caratteristiche del C++; new e delete in vece rispettivamente di malloc (o calloc) e free, e non mischiare assolutamente le cose.
ok ma perché? in università mi hanno sempre sconsigliato di usare la new e advanced linux programming preferisce usare la malloc. Se c'è un buon motivo per cui non dovrei utilizzare la malloc in c++ la smetto immediatamente.
@Barbalbero: potresti usare Valgrind per scoprire il problema.
Anche ieri ho trovato su un forum che consigliavano di utilizzare valgrind, ma essendo un debugger non credo che potrà aiutarmi. Però ora lo installo e se non è troppo complicato vedo se può essermi d'aiuto.
Secondo me il problema comunque è molto banale, guardate il codice nuovo. Sono 4 righe di codice, evidentemente faccio qualcosa di non permesso, non lo so, per esempio mettere un array in una struttura dentro un'altra struttura, non ne ho idea
Barbalbero
04-04-2011, 11:09
ok forse ho un'illuminazione. Forse il problema sta nella libreria GMP. Probabilmente devo fare qualcosa prima di deallocare questo tipo di dato.
Ora cerco sulla guida poi vi faccio sapere
Dove deallochi struttura->matrice ?
Barbalbero
04-04-2011, 11:30
Dove deallochi struttura->matrice ?
la deallocherei dopo avere deallocato i vari elementi, ovvero dopo il codice che genera l'errore
Barbalbero
04-04-2011, 12:03
— Function: void mpf_clear (mpf_t x)
Free the space occupied by x. Make sure to call this function for all mpf_t variables when you are done with them.
Probabilmente lo spazio deve essere deallocato con questa funzione e non è necessaria nessuna ulteriore free. Probabilmente.
tomminno
04-04-2011, 13:38
ok ma perché? in università mi hanno sempre sconsigliato di usare la new e advanced linux programming preferisce usare la malloc. Se c'è un buon motivo per cui non dovrei utilizzare la malloc in c++ la smetto immediatamente.
Se all'università ti hanno sconsigliato di usare new sono dei cialtroni. In C++ new è l'operatore per allocare dinamicamente la memoria.
malloc è la funzione di allocazione del C.
Il primo problema che si presenta utilizzando malloc invece di new è che non viene richiamato il costruttore della classe, pertanto cadono un bel po' di postulati del C++.
Per quanto riguarda advanced linux programming molto probabilmente parla delle librerie di sistema che sono in C.
Anche ieri ho trovato su un forum che consigliavano di utilizzare valgrind, ma essendo un debugger non credo che potrà aiutarmi. Però ora lo installo e se non è troppo complicato vedo se può essermi d'aiuto.
Secondo me il problema comunque è molto banale, guardate il codice nuovo. Sono 4 righe di codice, evidentemente faccio qualcosa di non permesso, non lo so, per esempio mettere un array in una struttura dentro un'altra struttura, non ne ho idea[/QUOTE]
Sfortunatamente C++ duplica gran parte delle strutture di C, ma spesso lo fa saltando dei pezzi :D
Ci sono molti casi in cui quello che si vuole fare è proprio allocare della memoria non inizializzata per un'uso futuro, e in quel caso new oltre a essere lento è anche abbastanza pericoloso; ad esempio se fai new float*[n] ti dovrai ricordare di non chiamare delete, ma delete[].
Che per quanto mi riguarda è una schifezza, ed è anche inutilmente non trasparente rispetto a malloc/free, che se si sa cosa si sta facendo sono molto più chiari e sicuri (allochi n byte, se non riesce da NULL. Punto.).
Per non parlare di tutti quei casi in cui si manovrano i singoli byte, e allora malloc è perfetto.
E tra l'altro nemmeno new[] e delete[] chiamano i costruttori/distruttori :muro:
E vogliamo parlare dell'overload di new, o del fatto che new non ha un comportamento definito in caso di errore?
Io più imparo C++ più mi rendo conto di usare un "C con le classi", tanta, troppa roba "avanzata" in C++ è semplicemente stata realizzata male e complica soltanto la vita, partendo dalla STL per finire ai templates.
Quindi pls prima di dire boiate come "lol c'hai C++ usa new" pensateci :D
Barbalbero
04-04-2011, 17:23
Sfortunatamente C++ duplica gran parte delle strutture di C, ma spesso lo fa saltando dei pezzi :D
Ci sono molti casi in cui quello che si vuole fare è proprio allocare della memoria non inizializzata per un'uso futuro, e in quel caso new oltre a essere lento è anche abbastanza pericoloso; ad esempio se fai new float*[n] ti dovrai ricordare di non chiamare delete, ma delete[].
Che per quanto mi riguarda è una schifezza, ed è anche inutilmente non trasparente rispetto a malloc/free, che se si sa cosa si sta facendo sono molto più chiari e sicuri (allochi n byte, se non riesce da NULL. Punto.).
Per non parlare di tutti quei casi in cui si manovrano i singoli byte, e allora malloc è perfetto.
E tra l'altro nemmeno new[] e delete[] chiamano i costruttori/distruttori :muro:
E vogliamo parlare dell'overload di new, o del fatto che new non ha un comportamento definito in caso di errore?
Io più imparo C++ più mi rendo conto di usare un "C con le classi", tanta, troppa roba "avanzata" in C++ è semplicemente stata realizzata male e complica soltanto la vita, partendo dalla STL per finire ai templates.
Quindi pls prima di dire boiate come "lol c'hai C++ usa new" pensateci :D
concordo in tutto. Inoltre se il problema è solo che la malloc non richiama il costruttore, per allocare struct e array penso che sia molto meglio la malloc rispetto alla new.
Tornando al problema iniziale. L'errore era dovuto al fatto che gli elementi della matrice sono di un tipo definito nella libreria GMP. Se dopo aver chiamato la mpf_clear su ogni elemento della matrice, tento di applicare la free alla matrice mi fa il solito errore double free. Quindi penso che mpf_clear applichi la free anche alla matrice stessa, che ne dite?
||ElChE||88
04-04-2011, 18:32
E tra l'altro nemmeno new[] e delete[] chiamano i costruttori/distruttori :muro:
Eh? Ovvio che li chiamano. È proprio per quello che le versioni [] sono separate.
Io più imparo C++ più mi rendo conto di usare un "C con le classi", tanta, troppa roba "avanzata" in C++ è semplicemente stata realizzata male e complica soltanto la vita, partendo dalla STL per finire ai templates.
Quindi pls prima di dire boiate come "lol c'hai C++ usa new" pensateci :D
Magari fosse soltanto un C con le classi, invece tocca sorbirsi alcune schifezze sintattiche, per non parlare dei compilatori che dopo tutti questi anni non riescono a tirare fuori messaggi di diagnostica comprensibili agli esseri umani, specie se si usano i template.
Eh? Ovvio che li chiamano. È proprio per quello che le versioni [] sono separate.
Testato personalmente, anche se nella classe definisci il costruttore vuoto, comunque non viene chiamato e i valori dentro sono garbage.
Prova e verrai piacevolmente sorpreso :D
||ElChE||88
04-04-2011, 21:48
Testato personalmente, anche se nella classe definisci il costruttore vuoto, comunque non viene chiamato e i valori dentro sono garbage.
Prova e verrai piacevolmente sorpreso :D
:mbe:
#include <iostream>
class Test
{
public:
Test() { std::cout << "ctor" << std::endl; };
~Test() { std::cout << "dtor" << std::endl; };
};
int main(int argc, char **argv)
{
int count;
std::cin >> count;
Test *test = new Test[count];
delete[] test;
return EXIT_SUCCESS;
}
Sia costruttore che distruttore vengono chiamati per ciascun elemento.
(codice schifoso, ma è giusto un test)
tomminno
04-04-2011, 23:03
Ci sono molti casi in cui quello che si vuole fare è proprio allocare della memoria non inizializzata per un'uso futuro, e in quel caso new oltre a essere lento è anche abbastanza pericoloso; ad esempio se fai new float*[n] ti dovrai ricordare di non chiamare delete, ma delete[].
Se usassi vector,list o array potresti dimenticarti perfino di delete[].
Che per quanto mi riguarda è una schifezza, ed è anche inutilmente non trasparente rispetto a malloc/free, che se si sa cosa si sta facendo sono molto più chiari e sicuri (allochi n byte, se non riesce da NULL. Punto.).
Certo buttiamo via i costruttori che non servono proprio a niente...
Chi ti vieta di allocare con new un array di n byte???
Per non parlare di tutti quei casi in cui si manovrano i singoli byte, e allora malloc è perfetto.
Eventuali differenze con new???
E tra l'altro nemmeno new[] e delete[] chiamano i costruttori/distruttori :muro:
Se intendi allocare un array di qualcosa il costruttore di default viene chiamato eccome.
E vogliamo parlare dell'overload di new, o del fatto che new non ha un comportamento definito in caso di errore?
A me pare che bad_alloc sia l'eccezione sollevata se new fallisce. Dove starebbe il comportamento indefinito in caso d'errore?
Io più imparo C++ più mi rendo conto di usare un "C con le classi", tanta, troppa roba "avanzata" in C++ è semplicemente stata realizzata male e complica soltanto la vita, partendo dalla STL per finire ai templates.
Eh si certo STL e template complicano proprio la vita :doh:
Sarà mica appunto che te usi il "C con le classi" invece del C++?
Quindi pls prima di dire boiate come "lol c'hai C++ usa new" pensateci :D
In C++ non esiste ragione per usare malloc al posto di new.
E se te sostieni che è meglio usare malloc forse è il caso di studiarsi un po' meglio il C++, oppure limitarsi a parlare di C.
tomminno
04-04-2011, 23:08
concordo in tutto. Inoltre se il problema è solo che la malloc non richiama il costruttore, per allocare struct e array penso che sia molto meglio la malloc rispetto alla new.
Se vuoi un array usa la classe array.
Inutile continuare con la pessima abitudine di usare malloc in C++.
Tornando al problema iniziale. L'errore era dovuto al fatto che gli elementi della matrice sono di un tipo definito nella libreria GMP. Se dopo aver chiamato la mpf_clear su ogni elemento della matrice, tento di applicare la free alla matrice mi fa il solito errore double free. Quindi penso che mpf_clear applichi la free anche alla matrice stessa, che ne dite?
Vedi che era il codice che non avevi postato a creare i problemi? :D
Data la documentazione mi pare ovvio:
Free the space occupied by a NULL-terminated list of mpf_t variables.
:mbe:
Sul serio, pensavo fosse default. Ma poi un bug mi ha fatto cambiare idea. Non lo so, magari lo fa "a volte" :asd:
Bisognerebbe indagare...
@tomminno
Prima di tutto, c'è da dire che uso C/C++ perchè è multipiattaforma, e perchè ha più librerie compatibili di qualsiasi altro linguaggio esistente...
il che rende molto difficile usare allo stesso tempo degli "advanced tricks" di C++ senza complicarsi la vita con librerie di terzi.
ad esempio la std è piuttosto oscena, e preferisco non usarla.
A parte che è lenta, a parte che genera errori incomprensibili, ha anche il problema che non è usata da circa nessuna libreria di terze parti, almeno nel mio ambito.
Alcuni esempi:
-OpenGL
-DirectX
-Win32
-OpenAL
-Box2D
-Havok
-PhysX
-CUDA
-Qt
-l'iphone
-android NDK
in sostanza, quelle librerie che esistono in C/++ per le sue performance, o per la compatibilità con C in genere NON usano STL;
di conseguenza se i dati interni al programma sono immagazzinati in contenitori STL, tutto il codice di interazione diventa un cast, una copia, o peggio... e questo perchè STL ha solo contenitori opachi.
L'unico tipo veramente usato di STL è la stringa, quella si salva... ma in generale la situazione è completamente diversa da Java, e STL fallisce proprio perchè è tutto tranne che standard.
Ovviamente esistono ambiti dove le performance non contano, ma allora li è da malati usare C++ e basta...
I templates complicano la vita sostanzialmente a causa del compilatore, ad esempio in quei casi quando 2 template devono includersi a vicenda.
Bellissimo :asd:
Inoltre come sopra generano errori assurdi e uccidono il tempo di compilazione, che per progetti grandi E' importante.
Diverse strutture nei miei progetti non si potrebbero fare senza template, intendiamoci...
ma vanno usati con attenzione e non come fa STL o Boost, perchè a tutti gli effetti sono una feature buggata.
Per non parlare di tutti quei casi in cui si manovrano i singoli byte, e allora malloc è perfetto.
Perchè dovrei usare new invece, che è più lento, oltre ad essere meno chiaro.
Mi stai paragonando
void* data = (void*)new char[ size ];
a
void* data = malloc(size);
insomma. Il primo così, a occhio, è osceno, almeno per quanto mi riguarda. "new" mi dice a pelle "ehi sto allocando un oggetto".
Poi se vuoi usarlo come malloc si può fare, ma per me confonde e basta.
A me pare che bad_alloc sia l'eccezione sollevata se new fallisce. Dove starebbe il comportamento indefinito in caso d'errore?
Sapevo che in realtà le implementazioni di C++ restituiscono NULL, comunque non è un problema perchè di tutti i bug, new che fallisce non m'è mai capitato :asd:
Comunque sto sito (http://yosefk.com/c++fqa/index.html), anche se esagera, fa comunque capire come la penso quando parlo di "inconsistenze" :D
tomminno
05-04-2011, 10:09
Prima di tutto, c'è da dire che uso C/C++ perchè è multipiattaforma, e perchè ha più librerie compatibili di qualsiasi altro linguaggio esistente...
il che rende molto difficile usare allo stesso tempo degli "advanced tricks" di C++ senza complicarsi la vita con librerie di terzi.
Nel senso?
ad esempio la std è piuttosto oscena, e preferisco non usarla.
Direi che sono qui si entra nei gusti. Evidentemente te preferisci allocare gli array manualmente, io preferisco vector o array anche per interfacciarmi con il C.
Se vuoi convertire un intero in stringa non usi stl? Quindi usi le funzioni C come sprintf.
A parte che è lenta,
Riesci a quantificare quanto?
a parte che genera errori incomprensibili,
Si è uno dei difetti del linguaggio, ma meglio un errore segnalato dal compilatore che errori a runtime.
In ogni caso la segnalazione degli errori ultimamente pare essere migliorata in chiarezza.
ha anche il problema che non è usata da circa nessuna libreria di terze parti, almeno nel mio ambito.
Alcuni esempi:
-OpenGL
-DirectX
-Win32
-OpenAL
-Box2D
-Havok
-PhysX
-CUDA
-Qt
-l'iphone
-android NDK
Alcune librerie elencate sono in C, altre risalgono a periodi precedenti le STL.
Comunque si, in C++ ad ognuno piace reinventare la ruota.
in sostanza, quelle librerie che esistono in C/++ per le sue performance, o per la compatibilità con C in genere NON usano STL;
di conseguenza se i dati interni al programma sono immagazzinati in contenitori STL, tutto il codice di interazione diventa un cast, una copia, o peggio... e questo perchè STL ha solo contenitori opachi.
Perchè se dovessi interfacciare più librerie non faresti la stessa cosa?
Pensa un po' che nella programmazione a livelli le copie le fai comunque pur utilizzando gli stessi tipi base, anche in linguaggi meglio organizzati del C++.
L'unico tipo veramente usato di STL è la stringa, quella si salva... ma in generale la situazione è completamente diversa da Java, e STL fallisce proprio perchè è tutto tranne che standard.
STL fa parte dello standard, ma nessuno ti obbliga ad usarla.
Guarda anche Java ha il suo bel caos interno. Il più coerente strutturalmente rimane .Net, anche perchè è mono piattaforma e sviluppato da una sola azienda.
Ovviamente esistono ambiti dove le performance non contano, ma allora li è da malati usare C++ e basta...
Questi ambienti in definitiva non esistono. Le performance contano sempre.
ma vanno usati con attenzione e non come fa STL o Boost, perchè a tutti gli effetti sono una feature buggata.
Ritenere STL e Boost fatti male significa peccare un pò di saccenza.
Perchè dovrei usare new invece, che è più lento, oltre ad essere meno chiaro.
Mi stai paragonando
void* data = (void*)new char[ size ];
a
void* data = malloc(size);
insomma. Il primo così, a occhio, è osceno, almeno per quanto mi riguarda. "new" mi dice a pelle "ehi sto allocando un oggetto".
Poi se vuoi usarlo come malloc si può fare, ma per me confonde e basta.
Per me è osceno il void* a prescindere.
Sapevo che in realtà le implementazioni di C++ restituiscono NULL, comunque non è un problema perchè di tutti i bug, new che fallisce non m'è mai capitato :asd:
No no solleva una bad_alloc.
Comunque sto sito (http://yosefk.com/c++fqa/index.html), anche se esagera, fa comunque capire come la penso quando parlo di "inconsistenze" :D
Nessuno sostiene che il C++ sia il linguaggio meglio progettato.
Sta di fatto che il discorso era new vs malloc in C++. E qui la risposta non può essere che una: new.
Anche perchè altrimenti vai ad aggiungere nei tuoi programmi una ulteriore inconsistenza del linguaggio :D
Sul serio, pensavo fosse default. Ma poi un bug mi ha fatto cambiare idea. Non lo so, magari lo fa "a volte" :asd:
Bisognerebbe indagare...
Il costruttore di default non fa niente, per questo ottieni garbage all'interno della struttura allocata, a meno che tu non indendessi dire altro...
ad esempio la std è piuttosto oscena, e preferisco non usarla.
A parte che è lenta, a parte che genera errori incomprensibili, ha anche il problema che non è usata da circa nessuna libreria di terze parti, almeno nel mio ambito.
Alcuni esempi:
-OpenGL
-DirectX
-Win32
-OpenAL
-Box2D
-Havok
-PhysX
-CUDA
-Qt
-l'iphone
-android NDK
in sostanza, quelle librerie che esistono in C/++ per le sue performance, o per la compatibilità con C in genere NON usano STL;
Diciamo che in tre quarti degli esempi che hai elencato si tratta piu' che altro di librerie C (e non C++), framework pre-STL (QT, iphone) o minimalisti (NDK). Non conosco le altre librerie per cui su quelle non posso esprimermi.
In ogni caso la STL non mi e' mai parsa particolarmente lenta (anche se io non la uso tanto spesso), hai qualche riferimento a riguardo ?
Perchè dovrei usare new invece, che è più lento, oltre ad essere meno chiaro.
Le performance di new/delete e malloc/free sono analoghe. In ogni caso all'interno di codice critico non usi ne' una ne' l'altra.
Quando devi allocare della memoria grezza puo' essere piu' pratico usare malloc, ma non e' una cosa che serve fare molto spesso (nel senso di linee di codice, non di volte che e' usata).
Sapevo che in realtà le implementazioni di C++ restituiscono NULL, comunque non è un problema perchè di tutti i bug, new che fallisce non m'è mai capitato :asd:
Lo standard e' chiaro a riguardo, e puoi scegliere se farti lanciare un'eccezione (il default) piuttosto che ritornare NULL. Comportamenti diversi sono un bug del compilatore.
Barbalbero
05-04-2011, 13:03
Vedi che era il codice che non avevi postato a creare i problemi? :D
Data la documentazione mi pare ovvio:
A me invece pareva ovvio di no ma il dubbio era causato dall'errore detto all'inizio che la deallocazione della matrice mi causava.
Infatti utilizzando valgrind è apparso chiaro che le matrici, come ovvio che fosse, non vengono deallocate e vanno deallocate tramite free, ma solo dopo aver eseguito correttamente tutte le deallocazioni dei singoli oggetti della matrice. In questo caso non si genera nessun errore.
Riassumendo:
Grazie a Valgrind che è un tool favoloso.
Grazie a banryu79 per avermelo consigliato.
Grazie a tutti per i consigli ;)
E grazie alla malloc che è stata ingiustamente incolpata.
malloc tutta la vita :p
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.