2012-03-28 8 views
11

Zum Beispiel Ich brauche zwei Stücke von Speicher malloc, so:Um robuste C-Programm zu schreiben, wie vermeiden Sie zu viele verschiedene freie() Kombinationen?

void *a = malloc (1); 

if (!a) 
    return -1; 

void *b = malloc (1); 

if (!b) 
{ 
    free (a); 
    return -1; 
} 

Hinweis, wenn die zweite malloc fehlschlägt, muss ich „a“ zuerst befreien. Das Problem ist, kann dies sehr chaotisch sein, wenn es viele solcher malloc ist und die Fehlerprüfung des sind, es sei denn ich die berüchtigten „goto“ Klausel verwenden und sorgfältig die Reihenfolge der freien des arrangieren zusammen mit den Labels:

void *a = malloc (1); 

if (!a) 
    goto X; 

void *b = malloc (1); 

if (!b) 
    goto Y; 

return 0; //normal exit 

Y: 
    free (a); 
X: 
    return -1; 

Haben Sie eine bessere Lösung für diese Situation? Danke im Voraus.

+0

+1 sobald ich auch gleiches Problem, aber abgeschlossene Arbeit mit "unordentlich Code" konfrontiert .. !! –

Antwort

18

Wir tun wie folgt aus:

void *a = NULL; 
void *b = NULL; 
void *c = NULL; 
a = malloc(1); 
if (!a) goto errorExit; 
b = malloc(1); 
if (!b) goto errorExit; 
c = malloc(1); 
if (!b) goto errorExit; 

return 0; 
errorExit: 
//free a null pointer is safe. 
free(a); 
free(b); 
free(c); 
return -1; 
+4

+1, um darauf hinzuweisen, dass free Nullzeiger akzeptiert. Ich hatte keine Ahnung, dass das der Fall war. – Leo

+0

Auch wenn dies nicht der Fall ist (oder wenn Sie einige andere Ressourcen freigeben müssen, bei denen die entsprechende Bereinigungsfunktion dies nicht tut), können Sie immer 'if (a) free (a);' und so weiter machen. –

13

Verwenden von goto ist meiner Meinung nach keine schlechte Sache. Die Verwendung zur Ressourcenbereinigung ist genau das Richtige.

Quellcode so berühmt wie der Linux-Kernel verwendet die Technik.

Verwenden Sie einfach nicht goto, um rückwärts zu gehen. Das führt zu Katastrophen und Verwirrung. Nur vorwärts springen ist meine Empfehlung.

+4

Das tut nicht nur der Linux-Kernel, sondern auch FreeBSD. Stimme voll und ganz zu, dass es normal ist. – arrowd

+0

Ich habe genau die gleiche lange Zeit gemacht, als ich noch reines C benutzt habe. Jetzt benutze ich (mit C++) intelligente Zeiger. – Patrick

+1

Auch; ist nicht frei (NULL) ein No-Op? Bedeutet das, dass es kein Problem gibt, einfach alles zu befreien? – Alxandr

3

Wie bereits erwähnt von Zan Lynx verwenden Sie die Anweisung goto.

Sie können auch größere Speicherbereiche für die weitere Verwendung zuweisen.

Oder Sie können Ihre Zeit investieren, um etwas wie memory pool zu entwickeln.

3

Oder dies tun.

void *a,*b; 
char * p = malloc(2); 
if (!p) return -1; 
a = p; 
b = p+1; 
2

denke ich OOP-Techniken Ihnen eine schöne und saubere Lösung für dieses Problem geben könnte:

typedef struct { 
    void *a; 
    void *b; 
} MyObj; 

void delete_MyObj(MyObj* obj) 
{ 
    if (obj) { 
     if (obj->a) 
      free(obj->a); 
     if (obj->b) 
      free(obj->b); 
     free(obj); 
    } 
} 

MyObj* new_MyObj() 
{ 
    MyObj* obj = (MyObj*)malloc(sizeof(MyObj)); 
    if (!obj) return NULL; 
    memset(obj, 0, sizeof(MyObj)); 

    obj->a = malloc(1); 
    obj->b = malloc(1); 

    if (!obj->a || !obj->b) { 
     delete_MyObj(obj); 
     return 0; 
    } 

    return obj; 
} 

int main() 
{ 
    MyObj* obj = new_MyObj(); 
    if (obj) { 
     /* use obj */ 
     delete_MyObj(obj); 
    } 
} 
+0

Was ist, wenn Sie mehrere Objekte benötigen? Dann bist du wieder da, wo du angefangen hast. – Austin

2

Es gibt nichts wirklich falsch mit Ihrem goto Code IMO (ich würde ausführlichere Etiketten verwenden).

In diesem Fall erstellen die goto Anweisungen, die Sie geschrieben haben, genau die gleiche Struktur wie das Umkehren der if s.

Das heißt, ein bedingter Termin goto, die keinen Spielraum nicht verlassen hat genau die gleichen als if Anweisung ohne else. Der Unterschied ist, dass die gotopassiert nicht Bereich zu verlassen, während die if gezwungen ist, Umfang zu verlassen. Deshalb ist das if normalerweise einfacher zu lesen: Der Leser hat mehr Hinweise auf der Vorderseite.

void *a = malloc (1); 
if (a) { 
    void *b = malloc (1); 
    if (b) { 
     return 0; //normal exit 
    } 
    free(a); 
} 
return -1; 

Für ein paar Ebene dies in Ordnung ist, obwohl Sie „Pfeil-Code“ mit zu vielen Ebenen der Vertiefung bekommen zu weit gegangen. Das wird aus ganz anderen Gründen unlesbar.

Verwandte Themen