Fehlerbehandlung in C mit

Gast #2688401
Lesenswert?

Hallo,

ich hätte da eine Frage bezüglich der Fehlerbehandlung in C. Ich kenne 
die Konzepte der Fehlerbehandlung ( return, longjmp, lokaler Fehlerindex 
). Was mir aber bei diesen fehlt ist, dass ich nicht weiss wer den 
Fehler verursacht hat (also welche Funktion). Bsp:
1
#include <stdlib.h>
2
#include <stdio.h>
3

4
int errorCode = 0;
5
char* errorFile = "\0";
6
char* errorLine = "\0";
7

8
void (*ErrorHandler) (void);
9
ErrorHandler errorHandler = NULL;
10
#define error(a) errorCode = a; errorFile = __FILE__; errorLine = __LINE__; errorHandler()
11

12
void doSomething(int count)
13
{
14
  ...
15
  error(12);
16
}
17

18
void handler()
19
{
20
  /* Fehlerausgabe etc. */
21
}
22

23
int main()
24
{
25
  errorHandler(handler);
26
  doSomething(45);
27
  ...
28
  
29
  return 0;
30
}

Ich hoffe es funktioniert auch, kann es jetzt nicht testen (grad kein 
Compiler zur Verfügung). Das Problem dieser Methode ist (oder eigentlich 
alle die ich kenne), dass ich keine Informationen, wer der Verursacher 
ist, bekomme. Sie kann mir zwar sagen, dass in doSomething() der Fehler 
aufgetreten ist und was für einer, allerdings erfahre ich nicht, dass es 
in Wirklichkeit main() ist. Also wenn ein falscher übergabe Wert 
übergeben wurde. Ich hoffe es ist verständlich. Bei C# sieht man gleich 
den ganzen Aufrufbaum der Funktionen (bzw. Methoden).
Kurz und knapp wie macht ihr das? Gut bei kleinen Programmen ist die 
einfache schon in Ordnung, aber bei größeren Projekten, wo eine Funktion 
1000 aufgerufen wird.

Vielen Dank für eure Antworten :).
Gast #2688430
Lesenswert?

er möchte einen CallStack!

Man müsste es selber Programmieren, in jeder funktion ruft man ein Makro 
auf was den funktionsnamen und eventeull noch die Parameter auf den 
"Stack" legt und diese am ende auch wieder entfernt. Und Genau das ist 
schon ein Problem denn man muss immer dafür sorgen das es wieder 
entfernt wird wenn die funktion verlassen wird.

C++ und den Destruktoren geht das etwas eleganter.
Gast #2688441
Lesenswert?

Genau das ist es was PeterII angesprochen hat. Allerdings ist es 
ziemlich aufwendig, man müsste in jeder Funktion (wie main()) den 
Funktionsnamen etc. setzen. Geht das nicht eleganter oder muss ich mich 
damit zufrieden geben? Wie sucht man dann den Fehler bei einem großen 
Programm, wo doSomething() 1000 aufgerufen wird? Raten und schauen ob 
man Recht hat?
#2688443
Lesenswert?

Wenn du jeder möglichen Fehlerstelle eine eindeutige Nummer zuornest, 
die du zurückgibst, dann weisst du zumindest wo genau der Fehler 
aufgetreten ist, auch wenn du keinen Call stack hast.
Wir haben da im allgemeinen ein eigenes Fehlermodul mit einer h Datei in 
der alle Fehlernummern als Konstante definiert sind.
Das Problem ist bei allen Fehlerkonzepten, auch bei "try ..  catch" etc. 
man muss sich konsequent an sein System halten.
Gast #2688448
Lesenswert?

Christopher C. schrieb:
> Allerdings ist es
> ziemlich aufwendig, man müsste in jeder Funktion (wie main()) den
> Funktionsnamen etc. setzen.
könnte man sogar per script mache, so aufwendig ist das nicht.

> Geht das nicht eleganter oder muss ich mich
> damit zufrieden geben?

in C nicht.

> Wie sucht man dann den Fehler bei einem großen
> Programm, wo doSomething() 1000 aufgerufen wird? Raten und schauen ob
> man Recht hat?
mit dem Debugger - dort einfach den CallStack anzeigen. (in der hoffnung 
das niemand der STack überschrieben hat)
#2688453
Lesenswert?

Christopher C. schrieb:
> Wie sucht man dann den Fehler bei einem großen
> Programm, wo doSomething() 1000 aufgerufen wird? Raten und schauen ob
> man Recht hat?

Problem nachstellen und debuggen
Oder einschaltbares Tracing mit unterschiedlichen Leveln. Dazu gibts 
auch entsprechende Bibliotheken, Ich programmiere allerdings in den 
letzten Jahren nur Java und wir benutzen log4j.

Zu den eindeutigen fehlernummern: Bei größeren Projekten übersichtlicher 
wird es wenn man die Nummer aus einer Modulnr und der Fehlernummer 
zusammensetzt. Also z.b. Modul 34 und Fehler 132 ergibt dann "034132".
Gast #2688455
Lesenswert?

Christopher C. schrieb:
> Naja perror() und strerror() bringt mir nicht so viel, wenn ich den
> Fehler in einem Fenster anzeige.
Bei perror kannst du ja noch zuätzlichen Erklärtext einfügen. Also 
könntest du das noch mit
1
__LINE__
2
__FILE__
 usw aufpeppen.
http://openbook.galileocomputing.de/c_von_a_bis_z/010_c_praeprozessor_004.htm#mjac2e36cecb18e7a294b1054b7758ee80

Kann aber auch sein, dass ich gar blicke was du wirklich willst. ;-)
#2688539
Lesenswert?

>  Ich hoffe es ist verständlich.

Ist es.

> Bei C# sieht man gleich den ganzen Aufrufbaum der Funktionen
> (bzw. Methoden).

Siehst du im Debugger normalerweise auch. Wenn das Problem beim Kunden 
auftritt, sieht die Sache aber anders aus.
Es gibt Runtime Systeme, die in solchen Fällen einen kompletten 
Stackdump machen. Den kann der Kunde schicken und dann kann man den 
analysieren. Manchmal hilft das sogar was. Denn in den meisten Fällen, 
ist der interessante Teil nicht der, das das Programm abgeschmiert ist, 
sondern der Weg wie es dazu gekommen ist. In einem realen Programm hat 
eine Division durch 0 eine Vorgeschichte, die nicht selten in 
irgendeiner vorhergehenden Benutzereingabe ihre Ursache hat (und das 
muss nicht notwendigerweise die letzte gewesen sein). Dort steckt 
irgendwo das eigentliche Problem.

Das ist das eine. Das andere ist: Ja C# kann das. Aber um welchen Preis? 
Da wo C Programme noch einen kleinen schnuckeligen Speicherfootprint 
haben, machen sich C# Programme im Speicher breit. C wurde eben in den 
60-er Jahren nach dem Muster entworfen: You don't get for what you 
didn't ask. Das Runtime System macht nur das, was absolut notwendig ist 
um die Funktion zu erfüllen. Willst du mehr, dann musst du dir das 
selber machen. Hat Nachteile, hat aber auch Vorteile: Du "erbst" nicht 
riesige Bibliotheken mit einem unbedachten Funktionsaufruf.
#2688572
Lesenswert?

> Wie sucht man dann den Fehler bei einem großen
> Programm, wo doSomething() 1000 aufgerufen wird?

Letzten Endes gibts nur eines: testen, testen, testen.
In Idealfall ist nicht der Programmierer selbst der Tester, sondern du 
hast eigene Leute dafür. Wenn die spitz kriegen "Operation X ist im 
Zusammenhang Y nicht sinnvoll", dann testen die 100-erte Variationen von 
X im Zusammenhang Y, alles was ihnen einfällt, um dein Programm zum 
Absturz zu bringen (und genau das ist das, was Programmierer 
normalerweise nicht tun) und sagen dir hinterher, wie sie den Absturz 
geschafft haben.

assert wurde ja schon angesprochen. Ist ein hilfreiches Werkzeug in der 
Debug-Phase, weil eine Funktion im Debugger laut 'Hilfe hier' schreit, 
weil irgendwelche Dinge nicht so sind, wie sie eigentlich sein sollten. 
Damit kriegt der Entwickler schon in der Entwicklungsphase eine Menge 
Probleme in den Griff, die sonst erst beim Kunden auftreten würden und 
dort ungleich mehr Schaden anrichten.
Gast #2688588
Lesenswert?

Karl Heinz Buchegger schrieb:
> Letzten Endes gibts nur eines: testen, testen, testen.

nein hilft leider nicht.

Wenn man z.b. in eine Datei schreiben will und das write einen Fehler 
liefert "fehler beim schreiben" dann hilft einem das überhaupt nicht 
weiter. Die Write funktion selber kennt auch nur das fiele-handle damit 
kann man auch nicht anfangen.
In so einen Fall braucht man für eine sinnvolle fehleranalyse einen 
CallStack wo man sieht in welchen zusammenhang etwas in die Datei 
geschrieben weden soll.

Und darin sehe ich wirklich ein Problem in C/C++ - die Fehlersuche ist 
viel komplizierter als in C# oder Java.

Ich hatte es mal versucht mit Makros und C++ hinzubekommen, das man eine 
CallStack bekommt - ja es geht aber es ist ein zusätzlicher Aufwand und 
man muss sich zwingen es wirklich überall einzusetzen.
#2688865
Lesenswert?

Peter II schrieb:
> Und darin sehe ich wirklich ein Problem in C/C++ - die Fehlersuche ist
> viel komplizierter als in C# oder Java.

Das stimmt, mit einem Stacktrace hat man schnell einen Anhaltspunkt wo 
man mit der Suche anfangen muss. Und wenn dann die Exception noch ein 
"Null-Pointer", eine "Array out of Bounds", eine "Data not numeric" oder 
"Data truncation at Table, Row" liefert, dann hat man auch den Grund und 
kann davon ausgehend zurückschauen.
Das hilft nicht immer aber sehr oft. Man spart sich nach meinen 
Erfahrungen bai 30 -50% von Kundenproblemen das zuhause nachstellen, 
bzw. kann das Problem gezielt und schnller nachstellen.

Aber was auf einem modernen PC kein Problem ist (große Runtime, viele 
DLLs) ist auf einem µC oder auch einem ARM wieder was ganz anderes. 
Genauso wie Geschwindigkeit wenn es mal wirklich darauf ankommt.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren