Newsgroup: it.comp.lang.c++
Titolo: puntatori e "delete", fenomeno (per me) misterioso...

Questo programmino accetta una frasetta da tastiera e la inverte,
utilizzando puntatori e liste. Fin qua nessun problema. Quando invece
voglio ripulire il tutto con i dovuti "delete", apriti cielo. La mia
funzione "cancella" per un po' passa correttamente al nodo successivo
cancellandolo, poi ad un certo punto ignora il puntatore corretto e si
va ad infilare immancabilmente all'indirizzo 0x25e0190. A questo punto
entra in loop. Solo per interrompere il loop ho inserito un contatore
che blocca il loop a 25 cicli, qualunque sia la lunghezza della frase
inserita.
Se elimino il comando delete, la funzione gira a meraviglia,
ovviamente senza cancellare niente. Perchč??
Il tutto con Dev-C++ 4.
Allego sorgente, scaricabile anche da
http://members.xoom.virgilio.it/fik/inverti_stringa.cpp
//                            INIZIO 
#include <iostream.h>
struct Nodo {
            char let;
            Nodo *proxLet;
            };
//===================================================================
// Prototipi di funzioni
void leggiFrase(Nodo *&testa);                
void stampaFrase(Nodo *testa);
void invertiFrase(Nodo *testa, Nodo *&nTesta);
void cancella(Nodo *inizio);
//===================================================================
int main()
{
Nodo *testa, *nTesta;
leggiFrase(testa);
invertiFrase(testa, nTesta);
cout << endl << "CHIAMATA ALLA FUNZIONE DI STAMPA PER LA LISTA DRITTA"
     << endl;
stampaFrase(testa);
cout << endl << "CHIAMATA ALLA FUNZIONE DI STAMPA PER LA "
     <<	"LISTA ROVESCIATA" << endl;
stampaFrase(nTesta);
cout << endl << "CHIAMATA ALLA FUNZIONE DI CANCELLAZIONE PER LA "
     << "LISTA DRITTA"
     << endl << endl << "Indirizzo iniziale passato dal main (testa)="
     << testa << endl;
cancella(testa);
cout << endl << "CHIAMATA ALLA FUNZIONE DI CANCELLAZIONE PER LA "
     << "LISTA ROVESCIATA"
     << endl << endl << "Indirizzo iniziale passato dal main "
     << "(nTesta)="
     << nTesta << endl;
cancella(nTesta);
return 0;
}
//==========================================================
// Inserimento da tastiera della frase da invertire.
void leggiFrase(Nodo *&testa)
{
int i=0;
char *frase, tappo='#';
Nodo *serie, *temp;
cin.unsetf(ios::skipws);
cout << "Inserisci la frase da invertire, (" << tappo << " per
concludere):"
     << endl;
frase=new char;
cin >> frase[i];
while (frase[i]!=tappo)
      {
      i++;
      cin >> frase[i];
      }
int max=i;
testa=0; serie=0; temp=0;
serie=new Nodo;
testa=serie;
i=0;
serie->let=frase[i]; i++;
while (i<max)
      {
      temp=new Nodo;
      serie->proxLet=temp;
      temp->let=frase[i];
      temp->proxLet=0;
      serie=temp;
      i++;
      }
}
//===================================================
// Stampa il contenuto della lista
void stampaFrase(Nodo *stInizio)
{
Nodo *serie;
cout << endl;
serie=stInizio;
while (serie!=0)
      {
      cout << serie->let;
      serie=serie->proxLet;
      }
cout << endl << endl << "indirizzo let proxLet" << endl;
serie=stInizio;
while (serie!=0)
      {
      cout << endl << serie << ", " << serie->let << ", " <<
serie->proxLet;
      serie=serie->proxLet;
      }
cout << endl << endl;
}
//==============================================================================
// Crea una lista invertita
void invertiFrase(Nodo *testa, Nodo *&nTesta)
{
Nodo *serie, *nSerie, *temp;
serie=testa;
nSerie=0;
cout << endl << endl << "ESEGUO LA FUNZIONE DI INVERSIONE
(invertifrase)" << endl;
while (serie!=0)
      {
      temp=new Nodo;
      temp->let=serie->let;
      temp->proxLet=nSerie;
      nTesta=temp;
      nSerie=temp;
      serie=serie->proxLet;
      }
}
//=============================================================
// NON CAPISCO PERCHE' DOPO ALCUNE ITERAZIONI QUESTA FUNZIONE VA
// ALL'INDIRIZZO 0x25e0190 ANZICHE' ALL'INDIRIZZO CONTENUTO IN
// proxLet, QUALUNQUE ESSO SIA!!!!!
void cancella(Nodo *inizio)
{
Nodo *temp;
int ferma=0;
// Utilizzo il contatore 'ferma'
// solo per bloccare l'esecuzione
// quando il ciclo while entra in loop
// infinito, con un "break".
cout << endl << "Puntatore iniziale (var. inizio)=" << inizio << endl;
temp=inizio;
while (temp!=0)
      {
      cout << endl << "cancello " << inizio << " (" << inizio->let
           << ") e passo a " << inizio->proxLet << ". ";
      delete inizio;
      temp=temp->proxLet;
      inizio=temp;
      // Esco dal ciclo while alla
      // 25esima iterazione.
      ferma++;
      if (ferma>25) break;
}
cout << endl << endl;
}
//              FINE
Grazie a chi mi dŕ qualche lume!
Monio

 frase=new char;
ma qui allochi spazio per un solo carattere !
Pela


Sì, in questa fase è così, ma poi ogni carattere si va ad infilare in
un nodo di una lista (vedi sorgente, ciclo while poco sotto). Qui
infatti il programma gira che è una meraviglia. E' solo nella funzione
"cancella" che va dove gli pare.
Ciao, Monio

 funzione "cancella" che va dove gli pare.
Ciò non mi soprende. Dopo un'operazione come quella descritta sopra, che
potenzialmente corrompe il contentuto dello heap, è ragionevole aspettarsi
problemi al momento del 'delete'.
Max

Max
Grazie Max, la tua idea pareva buona, infatti non so quale sega
mentale mi abbia fatto ricorrere a quello strano arzigogolo per
l'input. Ora ho modificato la funzione "leggiFrase", che riempie
direttamente una normale lista. Tutto fila liscio fino al delete, E il
fenomeno descritto prosegue immutato.
Qualche altra idea? :-(
Monio
//     INIZIO
#include <iostream.h>
struct Nodo {
            char let;
            Nodo *proxLet;
            };
//============================================
// Prototipi di funzioni
void leggiFrase(Nodo *&testa);
void stampaFrase(Nodo *testa);
void invertiFrase(Nodo *testa, Nodo *&nTesta);
void cancella(Nodo *inizio);
//===========================================



cancella(testa);

cancella(nTesta);
return 0;
}
//===============================================
void leggiFrase(Nodo *&testa)
// Inserimento da tastiera della
// frase da invertire.
{
char tappo='#';
Nodo *serie, *temp;
cin.unsetf(ios::skipws);
cout << "Inserisci la frase da invertire, (" << tappo
     << " per concludere):"
     << endl;
testa=0; serie=0; temp=0;
temp=new Nodo;
testa=temp;
cin >> temp->let;
while (temp->let!=tappo)
      {
      serie=temp;
      temp=new Nodo;
      temp->proxLet=0;
      cin >> temp->let;
      if (temp->let!=tappo)
         serie->proxLet=temp;
      }
}
//========================================
void stampaFrase(Nodo *stInizio)
// Stampa il contenuto della lista
{
Nodo *serie;
cout << endl;
serie=stInizio;
while (serie!=0)
      {
      cout << serie->let;
      serie=serie->proxLet;
      }
cout << endl << endl << "indirizzo let proxLet" << endl;
serie=stInizio;
while (serie!=0)
      {
      cout << endl << serie << ", " << serie->let << ", "
           << serie->proxLet;
      serie=serie->proxLet;
      }
cout << endl << endl;
}
//=============================================
void invertiFrase(Nodo *testa, Nodo *&nTesta)
// Crea una lista invertita
{
Nodo *serie, *nSerie, *temp;
serie=testa;
nSerie=0;
cout << endl << endl << "ESEGUO LA FUNZIONE DI INVERSIONE "
     << "(invertifrase)" << endl;
while (serie!=0)
      {
      temp=new Nodo;
      temp->let=serie->let;
      temp->proxLet=nSerie;
      nTesta=temp;
      nSerie=temp;
      serie=serie->proxLet;
      }
}
//===============================================
// NON CAPISCO PERCHE' DOPO ALCUNE ITERAZIONI QUESTA
// FUNZIONE VA ALL'INDIRIZZO 0x25e0190 ANZICHE'
// ALL'INDIRIZZO CONTENUTO IN proxLet,
// QUALUNQUE ESSO SIA!!!!!
void cancella(Nodo *inizio)
{
Nodo *temp;
int ferma=0;
// Utilizzo il contatore 'ferma'
// solo per bloccare l'esecuzione
// quando il ciclo while entra in loop
// infinito, con un "break".
cout << endl << "Puntatore iniziale (var. inizio)="
     << inizio << endl;
temp=inizio;
while (temp!=0)
      {
      cout << endl << "cancello " << inizio << " ("
           << inizio->let
           << ") e passo a " << inizio->proxLet << ". ";
      delete inizio;
      temp=temp->proxLet;
      inizio=temp;
      ferma++;


       inizio=temp;
       temp=temp->proxLet;
       delete inizio;
       inizio=temp;
Max

Max
Grazie Max!!! Il problema stava proprio lě, ora fila che č una
meraviglia!!
Se ne hai tempo e voglia, mi spiegheresti anche come faceva prima a
fare correttamente qualche iterazione (di solito 5) prima di andare in
malora?
Grazie ancora, Monio

 malora?
Per sapere perchè prooprio dopo 5 e perchè è uscito un'indirizzo con quel
valore bisognerebbe conoscere nel dettaglio il meccaniscmo di gestione
della memoria dinamica, che varia da implementazione a implementazione.
Chissà, è possibile che fino alla quinta iterazione il sistema che ricicla
la memora allocata non abbia toccato il contenuto dei frammenti
rilasciati, mentre alla sesta ci avesse scritto qualcosa, forse per
agganciare il segmento interessato a una lista di segmenti riutilizzabili.
Max

Max
Grazie ancora! Monio