Avatar billede driis Nybegynder
25. august 2003 - 19:30 Der er 12 kommentarer og
1 løsning

Kædet liste - problem med new

Jeg har implementeret en kædet liste i C++. Jeg har stort set fået det til at virke, men: Hvis jeg først fylder en liste, tømmer den, og dernæst forsøger at fylde data i den igen - så får jeg en unhandled exception i _heap_alloc_base funktionen i filen malloc.c, når jeg forsøger at oprette et nyt dataelement med new.

Helt præcist får jeg meddelelsen:
Unhandled exception at 0x77f75a58 in lab1.exe: User breakpoint

Og i debug vinduet står følgende:
HEAP[lab1.exe]: HEAP: Free Heap block 370f08 modified at 370fd8 after it was freed
Unhandled exception at 0x77f75a58 in lab1.exe: User breakpoint.

Det skal siges, at jeg ikke har pillet ved definitionen af new eller andet. Jeg bruger MS VC++ 7.

Jeg forstår ikke helt hvad der er galt - specielt fordi det hele fungerer fint første gang der fyldes data i listen. Er der nogen der kan hjælpe ?
Avatar billede arne_v Ekspert
25. august 2003 - 19:37 #1
Gæt: du laver noget potentielt usikkert som tilfældigvis går
godt første gang men fejler anden gang.

Den siger jo at du bruger dynamisk allokeret memory efter at du har
deallokeret det.

Men vi skal nok se noget kode for at kunne hjælpe mere.
Avatar billede bertelbrander Novice
25. august 2003 - 19:38 #2
De fejl du nævner skyldes sansynligvis at du i har en fejl i din kode, fejlen kan f.ex. være at du:
1: Skriver ud over enden af en buffer
2: Skriver til en buffer efter at du har frigivet den
3: Frigiver den samme buffer flere gange
4: Forsøger at frigive en buffer som du ikke har allokeret

Vis os noget kode, så kan det være at vi kan hjælpe
Avatar billede driis Nybegynder
25. august 2003 - 20:05 #3
Udmærket, jeg ville bare ikke fylde spørgsmålet med kode, før jeg havde hørt om der var een, der liige vidste hvad problemet var.

Indhold af listbuffer.h :

// --------------- fil start
#include "buffer.hpp"
#include "listnode.h"
#include "listnode.cpp"

template <class T>
class ListBuffer : public Buffer<T>
{
public:
    ListBuffer();
    ~ListBuffer();
    void put(T value);
    T get();
    bool isEmpty();
    bool isFull();
    int count();
private:
    ListNode<T> * first ;
    ListNode<T> * last  ;
};
// ----------- fil slut

Implementation :

// ----------- fil start
#include "listbuffer.h"


// Listbuffer constructor
template <class T>
ListBuffer<T>::ListBuffer()
{
    first = last = 0 ;
}

// Listbuffer destructor
template <class T>
ListBuffer<T>::~ListBuffer()
{
    // Slet alle dynamisk allokerede listnodes
    while ( !this->isEmpty() )
        get() ;
}


// indsæt data
template <class T>
void ListBuffer<T>::put(T value)
{
    ListNode<T> * ny = new ListNode<T> ;
    ListNode<T> * tmp = 0 ;

    if ( !ny )
        exit(1) ;

    ny->putData(value) ;
    ny->setNext(0) ;

    tmp = last ;
    last = ny ;
    if ( tmp != 0 )
        tmp->setNext(ny) ;
   
    if ( first == 0 )
        first = last ;
}

// Hent data
template <class T>
T ListBuffer<T>::get()
{
    if ( !isEmpty() )
    {
        T d = first->getData()    ;
        ListNode<T> * n = first    ;
        first = first->getNext() ;
        delete n ;
        return d ;
    }else
    {       
        return 0 ;        // vi skal returnere noget.
    }
}

// er ListBuffer tom ?
template <class T>
bool ListBuffer<T>::isEmpty()
{
    return ( first == 0 ? true : false ) ;
}

// er ListBuffer fuld ?
template <class T>
bool ListBuffer<T>::isFull()   
{
    return false ;
}

// tæl antal elementer
template <class T>
int ListBuffer<T>::count()
{
    int i = 0 ;
    ListNode<T> * c = first ;

    if ( isEmpty() )
        return 0 ;
    else
    {
        i++ ;
        while ( c->getNext() != 0 )
        {
            i++ ;
            c = c->getNext() ;
        }
        return i ;
    }
}
// ---------- fil slut

Buffer er en abstrakt basisklasse, som danner grundlag for grænsefladen til ListBuffer. ListNode er en hjælpe klasse, den indeholder data og en pointer til næste element, som det også fremgår:

template <class T>
class ListNode
{
public:
    ListNode() ;                            // default constructor
    ListNode(T inData, ListNode<T> * nnext = 0);// constructor
    T    getData() ;
    bool putData(T nyData) ;
    ListNode<T> * getNext() ;
    void setNext(ListNode<T> * nnext) ;
private:
    T    data ;
    ListNode<T> * next ;
};
Avatar billede arne_v Ekspert
25. august 2003 - 20:18 #4
Jeg tror at dit problem er at last ikke bliver sat til 0 når listen er helt tom.

Og at put så fejler første gang du indsætter fordi last har en værdi.
Avatar billede arne_v Ekspert
25. august 2003 - 20:18 #5
tmp = last ;
    last = ny ;
    if ( tmp != 0 )
        tmp->setNext(ny) ;

er ikke god hvis last peger på noget memory der er deallokeret !
Avatar billede arne_v Ekspert
25. august 2003 - 20:19 #6
get skal vel bare have en:

if ( first == 0 )
  last = first;

[det kan kodes anderledes men den her er symmetrisk med put]
Avatar billede driis Nybegynder
25. august 2003 - 20:42 #7
arne_v >> Det var lige nøjagtig dét der var galt ( og jeg kan selv se problemet i det nu ). Skriv et svar, du har fortjent dine point.

Jeg har lige et sidespørgsmål : Grænsefladen i ListBuffer skal svare til den i basisklassen Buffer - og denne indeholder funktionen isFull, som skal returnere om bufferen er fuld. Hvordan kan jeg hensigtsmæssigt implementere isFull() på ListBuffer ? ListBuffer er jo først fuld, når der ikke er mere hukommelse til rådighed. Er der en metode, hvorpå man kan spørge allokeringssystemet om der er plads til rådighed til et enkelt element ?

Man kunne vel implementere det således:
// er ListBuffer fuld ?
template <class T>
bool ListBuffer<T>::isFull()   
{
    ListNode<T> * tmp = new ListNode<T> ;
    if ( !tmp )
        return true ;
    else
    {
        delete tmp ;
        return false ;
    }
}

... men jeg synes det er lidt voldsomt først at allokere plads og så slette det igen, blot for at finde ud af om der er plads til et element mere eller ej.
Avatar billede erikjacobsen Ekspert
25. august 2003 - 20:49 #8
du kan returnere false hver gang i isFull. Det er i hvert fald tilnærmelsesvis rigtigt.
Venter du til der ikke er mere virtuelt lager tilbage vil din applikation alligevel
køre så langsomt, så det kan være ligemeget.
Avatar billede arne_v Ekspert
25. august 2003 - 20:56 #9
svar
Avatar billede arne_v Ekspert
25. august 2003 - 20:59 #10
Du kunne måske spørge Windows om memory situationen.

Men det burde aldrig blive et problem.
Avatar billede arne_v Ekspert
25. august 2003 - 21:00 #11
PS: Du har helt styr på new og hvornår den returnerer NULL og hvornår
    den smider en exception ?
Avatar billede driis Nybegynder
25. august 2003 - 22:43 #12
Nej - som du nok kan se i eksemplet går jeg ud fra at new returnerer 0 hvis den ikke er i stand til at allokere hukommelse. Er det compiler eller library specifikt om den returnerer 0 eller smider en exception ? Eller kommer det an på noget andet.

Men jeg tror jeg følger erikjacobsen's råd med at returnere false hver gang - som i selv siger, burde det ikke være et problem.
Avatar billede arne_v Ekspert
25. august 2003 - 22:47 #13
Vi har lige haft en lille tråd om det her:
  http://www.eksperten.dk/spm/388138
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester