Forum: PC-Programmierung Fehlerbehandlung in C mit


von Christopher C. (Gast)


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 :).

von Pcrom (Gast)


Lesenswert?

Suche mal das 'assert' commando im stdlib documentation, ich denke das 
ist genau was nu brauchst

von raute (Gast)


Lesenswert?

Öhm, wie wäre es mir errno per Hand setzen und dann mit perror() und 
strerror() ausgeben?

von Peter II (Gast)


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.

von Christopher C. (Gast)


Lesenswert?

Aber soweit ich weiss gibt assert Datei und Linie von dort aus wo auch 
assert aufgerufen wurde, also in doSomething().
Naja perror() und strerror() bringt mir nicht so viel, wenn ich den 
Fehler in einem Fenster anzeige.

von Christopher C. (Gast)


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?

von Udo S. (urschmitt)


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.

von Peter II (Gast)


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)

von Klaus W. (mfgkw)


Lesenswert?

M.W. gibt es sowohl in der glibc als auch bei VS Bibliotheksfunktionen, 
um den Call Stack zu holen - natürlich nicht portabel,

von Udo S. (urschmitt)


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".

von raute (Gast)


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. ;-)

von Christopher C. (Gast)


Lesenswert?

Danke so viele Rückmeldungen ;).
Der Trick mit der Modul-ID und der Callstack gefällt mir schon mal gut. 
Es müsste eine Plattformunabhängige Lösung sein, weshalb der Callstack 
rausfliegt, schade :(.

von Klaus W. (mfgkw)


Lesenswert?

Weil das alles in der Rubrik PC-PRogrammierung steht, gehe ich davon 
aus, daß es nicht um MC geht.

Dann frage ich mich aber, wieso man auf C besteht und nicht C++ nimmt.
Da kann man mit Ausnahmen viel schönere Sachen bauen.

von egbert (Gast)


Lesenswert?

Die beste Vorgehensweise zur Fehlerbehebung bzw. -Vermeidung im 
Zusammenhang mit C: Schreibe den Programm mit einer RICHTIGEN 
Programmiersprache und nicht mit diesem nutzlosen Frickler-Zeugs für 
langhaarige Kellerkinder mit schweren sozialen Störungen.

von Klaus W. (mfgkw)


Lesenswert?

(Rechner, auf denen C: zu finden ist, meide ich auch.)

Magst du deine Störungen nicht mal woanders ausleben, oder musst du 
jetzt jeden Thread, wo du nichts Intelligentes zu sagen hast, 
vollmüllen?

von Karl H. (kbuchegg)


Lesenswert?

Ach egbert.
Halt dich einfach raus, wenn du keine Ahnung hast.
In Zukunft werd ich deine unqualifizierten Meldungen kommentarlos 
löschen.

C ist eben nichts für Leute, die eine Tante brauchen, die mit ihnen Lulu 
geht.

von Udo S. (urschmitt)


Lesenswert?

Karl Heinz Buchegger schrieb:
> In Zukunft werd ich deine unqualifizierten Meldungen kommentarlos
> löschen.
Warum erst in Zukunft?

von Karl H. (kbuchegg)


Lesenswert?

Udo Schmitt schrieb:
> Karl Heinz Buchegger schrieb:
>> In Zukunft werd ich deine unqualifizierten Meldungen kommentarlos
>> löschen.
> Warum erst in Zukunft?

Auch wieder wahr.
Aber erst mal soll er wissen, dass er sich vergebliche Tipparbeit macht.

von Karl H. (kbuchegg)


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.

von Ka. (Gast)


Lesenswert?

Die GNU-C-Bibliothek stellt eine backtrace()-Funktion bereit:
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html

von Karl H. (kbuchegg)


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.

von Peter II (Gast)


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.

von Udo S. (urschmitt)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.