Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.net  
   

Die mobilen Seiten von c++.net:
https://m.c-plusplus.net

  
C++ Forum :: FAQ - C (C89, C99 und C11) ::  Dynamischer Speicher mit malloc/calloc/realloc/free     Zeige alle Beiträge auf einer Seite Auf Beitrag antworten
Autor Nachricht
Unregistrierter





Beitrag Unregistrierter 21:28:59 26.02.2008   Titel:   Dynamischer Speicher mit malloc/calloc/realloc/free            Zitieren

0. Read the fine manual

Als Begleitlektüre: malloc(3), calloc(3), realloc(3), free(3)

1. malloc() nicht casten

Man sieht es immer wieder, malloc() (bzw. der Rückgabewert) wird explizit in den Typen der empfangenden Variable gecastet. Das ist in C absolut unnötig, wird als schlechter Stil angesehen und kann zu Problemen führen.

1.1 Warum ist es unnötig?

Die Sprache C kennt seit ihrer ANSI-Standardisierung im Jahre 1989 den allseits bekannten void-Zeiger. Dieser hat die Eigenschaft, dass er implizit in Zeiger anderen Typs umgewandelt wird. Eine explizite Umwandlung ist damit unnötig.

Ausnahmen:
- der Code soll (aus welchen Gründen auch immer) kompatibel zu C++ sein.
- vor der ANSI-Standardisierung gab es den void-Zeiger nicht, malloc() gab damals einen char * zurück, welcher dann gecastet werden musste. Dies sollte aber wirklich nur noch bei der Wartung von sehr altem Code ein Thema sein.

1.2 Zu welchen Problemen kann es kommen?

Sollte man vergessen haben, die Header-Datei stdlib.h einzubinden, könnte der Compiler annehmen, dass malloc() ein Integer zurückgibt, was natürlich falsch ist. Ohne Typecast wird der Compiler den Code aller Wahrscheinlichkeit nach mit einer Warnung versehen. Mit Typecast aber würde eben diese Warnung verloren gehen, und der Fehler des vergessenen Headers wäre hier nicht mehr (durch Warnungen) zu erkennen.

Hinweis:
- aktuelle Compiler würden auch mit Typecast eine Warnung ausgeben. z.B. der gcc: "warning: implicit declaration of function ‘malloc’"


2. Hartkodierte Typen im Aufruf von malloc() vermeiden

Betrachten wird einen typischen Aufruf von malloc():

C++:
1
2
3
4
5
6
7
8
9
short *ptr;
 
// andere Deklarationen
 
// anderer Code
 
ptr = malloc ( sizeof(short) );
 
free ( ptr );


Zwar ist dieser Code nicht falsch, er enthält aber einen kleinen Fallstrick:
Würde der Programmierer die Deklaration von ptr in Zeile 1 zu einem späteren Zeitpunkt von short zu int ändern, aber vergessen das sizeof beim Aufruf von malloc() in Zeile 7 auch zu ändern, dann würde (je nach System) u.U. zu wenig Speicher für ein int reserviert werden und der Code (mit etwas Glück) crashen.

Dieses Problem kann vermieden werden, indem man den sizeof-Operator nicht auf einen Typ, sondern auf ein konkretes Objekt anwendet:

C++:
1
2
3
4
5
6
7
8
9
short *ptr;
 
// andere Deklarationen
 
// anderer Code
 
ptr = malloc ( sizeof(*ptr) );
 
free ( ptr );


Hier sollte klar sein, dass eine Änderung des Typs in Zeile 1 auch automatisch in Zeile 7 einfliesst. Wichtig ist hier zu erkennen, dass die Zeigervariable ptr bei sizeof immer dereferenziert werden muss.

Ausnahme:
- Soll Speicher für einen void-Zeiger reserviert werden, geht diese Variante nicht, da void * nicht dereferenziert werden kann.

3. Zu jedem malloc() ein free()

Da der Aufruf von malloc() (ebenso calloc() und evtl. auch realloc() [*]) Speicher reserviert, welcher auch über den Scope des Aufrufs erhalten bleibt, ist es notwendig diesen Speicher irgendwann (meistens gleich dann wenn er nicht mehr benötigt wird) wieder mit free() freizugeben. Wird dies nicht getan, entstehen Speicherlecks, welche je nach Programm unkritisch bis fatal sein können. In jedem Fall ist ein Speicherleck aber unschön und sollte vermieden werden.

[*] Normalerweise wird mit realloc() ein bereits reservierter Speicherbereich vergrößert oder verkleinert. Ist allerdings das erste Argument von realloc() gleich NULL, dann ist der Aufruf äquivalent zu einem Aufruf von malloc().

3.1 Loggen von Reservierungen/Freigaben

Eine einfache aber bereits wirksame Methode zum Überprüfen von Reservierungen/Freigaben ist es, die jeweiligen Funktionen zu wrappen und ihre Aufrufe einfach mitzuzählen. Die Implementierung könnte wie folgt aussehen:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* In der Datei wrapper.h würden natürlich die Prototypen der
Funktionen stehen und stdlib.h sowie stdio.h eingebunden */

#include "wrapper.h"
 
/* Zaehlervariablen */
static int num_alloc;
static int num_free;
 
void *wrapper_malloc ( size_t size ) {
    void *tmp = malloc ( size );
    if ( tmp != NULL ) {
        num_alloc++;
    }
    return tmp;
}
 
void *wrapper_calloc ( size_t nmemb, size_t size ) {
    void *tmp = calloc ( nmemb, size );
    if ( tmp != NULL ) {
        num_alloc++;
    }
    return tmp;
}
 
void *wrapper_realloc ( void *ptr, size_t size ) {
    void *tmp = realloc ( ptr, size );
    // wenn ptr gleich NULL, dann aequivalent zu malloc() behandeln
    if ( tmp != NULL && ptr == NULL ) {
        num_alloc++;
    }
    return tmp;
}
 
void  wrapper_free ( void *ptr ) {
    free ( ptr );
    num_free++;
}
 
void  wrapper_stats ( void ) {
    printf ( "Number of   allocations   : %10d\n", num_alloc );
    printf ( "Number of deallocations   : %10d\n", num_free  );
    printf ( "Therefore lost allocations: %10d\n", num_alloc - num_free );
}


3.2 realloc() richtig angewendet

Bei der Verwendung von realloc() zum vergrößern/verkleinern eines bereits reservierten Speicherbereiches übergibt man einen Zeiger auf eben diesen Speicherbereich.

C++:
// für ptr wurde bereits Speicher reserviert
ptr = realloc ( ptr, 10 * sizeof(*ptr) );


Das Problem bei diesem Code: Es könnte (warum auch immer) passieren, dass eine Vergößerung nicht möglich ist. In diesem Fall würde realloc() den Wert NULL zurückliefern. Dabei würde die in ptr abgelegte Adresse allerdings verlorengehen und damit auch die wahrscheinlich einzige Möglichkeit auf den Speicher dahinter zuzugreifen. Besser ist es, ptr erst dann die neue Adresse zuzuweisen, nachdem man das Ergebnis von realloc() getestet hat:

C++:
// für ptr wurde bereits Speicher reserviert
void *temp;
temp = realloc ( ptr, 10 * sizeof(*ptr) );
if ( temp != NULL ) {
    ptr = temp;
}


3.3 Tools zum prüfen auf Speicherlecks

Eine (noch) kleine Sammlung an Tools/Libs zum aufspüren von Speicherlecks:
- Valgrind
- DUMA
Werbeunterbrechung
C++ Forum :: FAQ - C (C89, C99 und C11) ::  Dynamischer Speicher mit malloc/calloc/realloc/free   Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können keine Beiträge in dieses Forum schreiben.
Sie können auf Beiträge in diesem Forum nicht antworten.
Sie können Ihre Beiträge in diesem Forum nicht bearbeiten.
Sie können Ihre Beiträge in diesem Forum nicht löschen.
Sie können an Umfragen in diesem Forum nicht mitmachen.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.net ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info und www.c-plusplus.net enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.