Fehlerbehandlung in C
-
Hi,
während man sich in C++ voll auf Destruktoren verlassen kann, gibt es einen solchen Mechanismus in C leider nicht. Auch wenn moderne Betriebssysteme den Speicher natürlich alleine leeren, würde mich doch mal interessieren, wie man in C mit Fehlern umgeht (z.B. malloc() oder fopen() geben 0 zurück), wenn man mal annehmen würde, dass das OS nicht so intelligent sei. Insbesondere wenn Funktionen in tieferen Ebenen solche Fehler abfangen müssen, endet das bei mir in einem riesigen if-else Gefrickel bis in die main() hinein. Und dabei gibt es auch noch Probleme was die transportation von Fehlercodes angeht. (Die main Funktion soll dann erfahren was 5 Funktionen unter ihr eigentlich schief gelaufen ist. oO)
-
dafür haben funktionen rückgabewerte...
meistens schauts so aus:
void *ret1 = funktion1(); if(!ret1){ return -1; } void *ret2 = funktion2(); if(!ret2){ free(ret1); return -2; } //use ret1, ret2 free(ret1); free(ret2); return 0;
..oder so:
void *ret1 = funktion1(); if(ret1){ void *ret2 = funktion2(); if(ret2){ //use ret1, ret2 free(ret1); free(ret2); return 0; } free(ret1); return -2; } return -1;
-
C-Nutzer schrieb:
riesigen if-else Gefrickel bis in die main() hinein.
-
In Standard C sind die Möglichkeiten zur Fehlerbehandlung nur rudimentär.
Das schließt natürlich nicht aus, dass der Compiler deines Vertrauens mehr anbietet.
setjmp/longjmp gestatten Sprünge auch über Funktionen hinaus, errno ist eine vom Laufzeitsystem selbst verwendete/befüllte Variable, die man abfragen kann.
Auch kannst du dir mal atexit/abort/assert/signal anschauen, vielleicht findest du da was.
goto ist sicherlich nicht zu empfehlen, aber bitte, jeder nach seiner Fasson.
-
also, ich bin nicht für einen nachbau eines try/catch in c! was eine sprache nicht anbietet, soll da auch nicht rein. c ist minimal, performant und nach einer gewöhnungsphase elegant!
mal angenommen man braucht 4 mallocs:
void *a,*b,*c,*d; a=b=c=d=0; if( 0 == (a = malloc(5)) || 0 == (b = malloc(5)) || 0 == (c = malloc(5)) || 0 == (d = malloc(5)) ){ if(a) free(a); if(b) free(b); if(c) free(c); if(d) free(d); return; }
-
King George² schrieb:
also, ich bin nicht für einen nachbau eines try/catch in c! was eine sprache nicht anbietet, soll da auch nicht rein. c ist minimal, performant und nach einer gewöhnungsphase elegant!
mal angenommen man braucht 4 mallocs:
void *a,*b,*c,*d; a=b=c=d=0; if( 0 == (a = malloc(5)) || 0 == (b = malloc(5)) || 0 == (c = malloc(5)) || 0 == (d = malloc(5)) ){ if(a) free(a); if(b) free(b); if(c) free(c); if(d) free(d); return; }
Dir sollte aber schon klar sein, daß Du damit ein paar Takte Performance wieder wegschmeißt, indem Du Zustandsinformation aus dem Programmablaufpfad herausnimmst und in Variableninhalte reinlegst.
Also bitte die ifs verschachteln oder goto nehmen. Oder besser "performant" aus der Aufzählung herausnehmen.
-
stellt euch vor, beim schreiben des letzten posts ist mir aufgefallen, dass ein großteil meines codes verbugged ist! und das alles ohne fehlermeldung und trotz valgrind...
also werden heute keine neuen features eingebaut, sondern wie sagt man da? refaktoriert?
-
axo, hatte a=b=c=d=0; vergessen...
-
volkard schrieb:
Dir sollte aber schon klar sein, daß Du damit ein paar Takte Performance wieder wegschmeißt, indem Du Zustandsinformation aus dem Programmablaufpfad herausnimmst und in Variableninhalte reinlegst.
Also bitte die ifs verschachteln oder goto nehmen. Oder besser "performant" aus der Aufzählung herausnehmen.performance verlier ich doch nur im fehlerfall, oder? also ich find es schön. hab gerade meinen code durchsucht (sind noch nicht so viele ksloc) und es ist genau 2 mal vorgekommen. einmal in einer funktion die noch nicht verwendet wird und das zweite mal in einer funktion, die nur einmal aufgerufen wird, da sie das global struct initialisiert.
-
King George² schrieb:
performance verlier ich doch nur im fehlerfall, oder?
Stimmt. Also irrelevant für Kostenbetrachtungen. Fehler von mir.
-
naja, a=b=c=d=0; kostet auch was! also hast schon recht - auch wenn ich sicher an anderer stelle mehr zeit verlier
-
C-Nutzer schrieb:
... würde mich doch mal interessieren, wie man in C mit Fehlern umgeht (z.B. malloc() oder fopen() geben 0 zurück ...
Die gängige Praxis ist die Benutzung von
goto
, schau Dir diesen Thread http://www.c-plusplus.net/forum/286407-full an, dort gibt es verschiedene Meinungen dazu. Dort habe ich auch mal etwas geschrieben und bin heute immer noch der Meinung, dass die Fehlerbehandlung mitgoto
nicht gut ist.
-
Obligatorisch:
if(a) free(a);
kann man auch schlicht durch
free(a);
ersetzen, weil
free(NULL);
per Standard nichts macht.
Ansonsten: Wenn die Umstände es erlauben, mache ich es häufig so, dass ich den gesamten Speicher auf einmal anfordere und danach Zeiger an verschiedene Stellen innerhalb des angeforderten Bereiches setze. Das vereinfacht die Behandlung insofern ganz ungemein, als dass Fälle, in denen zwei von vier mallocs funktionieren und der Rest nicht, überhaupt nicht auftreten können.
Beim Umgang mit Dateien oder anderen Ressourcen hilft einem das natürlich nicht, und ein bisschen fummelig ist die Sache in C halt. Von der goto-Methode halte ich nicht viel, aber ein Patentrezept habe ich auch nicht - es sei denn "benutz halt C++" zählt als Antwort.
-
seldon schrieb:
ersetzen, weil
free(NULL);
per Standard nichts macht.
War das nicht mal irgendwie eine Sicherheitslücke, oder waren es zwei mal free() hintereinander oder beides
p = malloc(N); free(p); free(p);
Ich würde mich auf den Standard nicht verlassen
-
p wird nicht NULL, bloß weil du free(p); ausführst.
-
seldon schrieb:
p wird nicht NULL, bloß weil du free(p); ausführst.
Ja, das ist klar, ich meine nur, es soll angeblich eine Sicherheitslücke gewesen sein, wenn man einen bereits mit
free
freigegebenen Zeiger noch mal mitfree
freigibt oder ähnlich - keine Ahnung, dachte, vielleicht kennt das jemand.
-
abc.w schrieb:
seldon schrieb:
p wird nicht NULL, bloß weil du free(p); ausführst.
Ja, das ist klar, ich meine nur, es soll angeblich eine Sicherheitslücke gewesen sein, wenn man einen bereits mit
free
freigegebenen Zeiger noch mal mitfree
freigibt oder ähnlich - keine Ahnung, dachte, vielleicht kennt das jemand.Das konnte mit alten Implementierungen von malloc/free ausgenutzt werden. Da hat free nicht geprüft, ob der Bereich noch reserviert ist und dann den Speicher zweimal in die Freelist geschrieben. Daher konnte es passieren, dass malloc zweimal die gleiche Adresse zurück gibt. Wenn das eine mal der Bereich für etwas kritisches benutzt wird und das andere mal der Bereich für Benutzereingaben, dann kann man so den kritischen Bereich überschreiben.
Aber mittlerweile dürften die meisten Implementierungen von malloc/free gefixt sein. Zumindest bei der GLIBC gibt es ja eine Erkennung für Double-free.
Aber if(a) free(a); schützt nicht vor einem double-free.
-
rüdiger schrieb:
Aber if(a) free(a); schützt nicht vor einem double-free.
Ich vermeide sowas durch "doppelte Buchhaltung", indem ich einfach jeden Pointer mit NULL initialisiere. Ein Makro, das dann mit einem Errorlevel auf ein cleanup- Label springt, kommt dann auch nicht ins Stolpern, weil ein fclose(NULL) bzw. free(NULL) keinen Blödsinn bauen kann.
Pferdefuß: Ein paar Takte gibt man beim Fn- Entry ab, aber damit lebe ich ganz gut.
C++ Exception throw/catch- Codes liefern eigentlich auch nicht essentiell anderen ASM- Code ab, also könnte es schon passen, auch wenn's ein paar hier den Magen umdreht ...
-
fclose(NULL) ist unspezifiziertes Verhalten.