Fehlerbehandlung in C
lima-city → Forum → Programmiersprachen → C/C++ und D
beispiel
benutzen
buffer
code
datei
erfahrung
fehler
fehlerfall
frage
funktion
http
machen
maschine
null
pointer
problem
programm
sprache
verbindung
wissen
-
gibt´s eigentlich Fehlerbehandlung in C (ohne ++), die ohne das dauernde Durchschleusen von Return-Codes über zig Funktionen hinweg (à la "if(ptr == NULL) return NULL;") auskommt?
Also so nach dem Prinzip
--- gefährliche Aufrufe ---
geht was schief springe zu Fehlerbehandlung.
--- Fehlerbehandlung ---
...
---
Kurz gesagt: ich brauche ein goto, nur eben nicht lokal, sondern eines das sogar aus Methoden rausspringen kann. Gibt´s sowas? -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
goto ist schwarze Magie. Das darfst du nur benutzen, wenn du wirklich weißt, was du tust ;)
Aber ja, es gibt ein mächtiges Werkzeug, dass dir einen solchen Sprung ermöglicht: http://www.cppreference.com/wiki/c/other/longjmp
Aber wieso rufst du im Fehlerfall nicht einfach eine normale Funktion auf, die dann den Dreck wieder wegmacht? -
goto ist schwarze Magie. Das darfst du nur benutzen, wenn du wirklich weißt, was du tust ;)
Es geht halt in diesen Fällen nicht anders.Aber ja, es gibt ein mächtiges Werkzeug, dass dir einen solchen Sprung ermöglicht: http://www.cppreference.com/wiki/c/other/longjmp
Ja, wenn das hält was es verspricht, ist das genau das richtige.Aber wieso rufst du im Fehlerfall nicht einfach eine normale Funktion auf, die dann den Dreck wieder wegmacht?
verstehe nicht, was du meinst.
Der Rest des Programms muss doch auf einer höheren Ebene iwie gemeldet bekommen, dass ein Fehler aufgetreten ist, damit es entsprechend darauf reagieren kann. ZB wenn es nicht möglich ist eine Datei zu öffnen, dann macht das fopen nur nicht einfach nicht, sondern gibt einen Hinweis aus.
Ach so, der Grund warum ich es nicht durch eine globale Variable mache, die dann auf höherer Ebene überprüft wird, ist, dass nach dem Fehler nicht sofort die Kontrolle zurückgegeben wird, sondern alles erstmal weiterläuft bis dieser Check durchgeführt wird.
Beitrag geändert: 6.11.2008 21:20:09 von danger-mouse -
Der Rest des Programms muss doch auf einer höheren Ebene iwie gemeldet bekommen, dass ein Fehler aufgetreten ist, damit es entsprechend darauf reagieren kann. ZB wenn es nicht möglich ist eine Datei zu öffnen, dann macht das fopen nur nicht einfach nicht, sondern gibt einen Hinweis aus.
Wieso höhere Ebene? Du solltest die Fehlerbehandlung direkt an der Wurzel des Problems reinprogrammieren. Kein wildes Gespringe oder ähnliches.
Dateiname=... while((foo=fopen(Dateiname,"mode"))==NULL) { Problembehandlung if(gib_auf) { Konsequenzen break; } }
-
Na toll. Sagen wir mal ich schreib eine Programmbibliothek, die auf einen Server zugreift und Dateien hochlädt.
Jetzt passiert etwas völlig unvorhergesehenes: Die Verbindung bricht ab.
Es ist mMn NICHT die Aufgabe des Programmierers der Bibliothek, eine Entscheidung zu treffen, wie das Problem gehandhabt wird. Vll ist es ein GUI-Programm, das die Bibliothek nutzt, und das will dem User einen Dialog anzeigen und ihn fragen, ob er es noch mal probieren will oä. -
Na toll. Sagen wir mal ich schreib eine Programmbibliothek, die auf einen Server zugreift und Dateien hochlädt.
Jetzt passiert etwas völlig unvorhergesehenes: Die Verbindung bricht ab.
Es ist mMn NICHT die Aufgabe des Programmierers der Bibliothek, eine Entscheidung zu treffen, wie das Problem gehandhabt wird. Vll ist es ein GUI-Programm, das die Bibliothek nutzt, und das will dem User einen Dialog anzeigen und ihn fragen, ob er es noch mal probieren will oä.
Ja, und deshalb hat Gott den Rückgabewert für Funktionen erfunden. Damit teilt eine "niedere" Funktion ihrem Aufrufer mit, was passiert ist. Die "höhere" aufrufende Funktion kann dann entscheiden, was sie im Fehlerfall unternimmt. Eine Reaktion wäre, sich ihrerseits mit einem entsprechenden Rückgabewert zu beenden und die Fehlerbehandlung einer "noch höheren" Funktion zu überlassen. Das ergibt am Ende das, was du "Durchschleusen der Return-Codes" nanntest. Das ist völlig normal und ich sehe da auch kein Problem. Die Weiterleitung von Exceptions funktioniert übrigens auch nicht anders.
Die Frage nach dem "globalen GOTO" hat dir der bladehunter mit longjmp() beantwortet. Das kann wirklich nützlich sein, wenn man weiß, wie man damit umgehen muss.
Bleibt die Frage, worauf du eigentlich hinauswolltest ...
Vielleicht interessierst du dich ja für Maschinensprache. Da kannst du Prozessorausnahmen (Traps, Exceptions, Software-Interrupts) basteln, die im Fehlerfall ganz nach Belieben die Programmfluss-Kontrolle an sich reißen können. Möchtest du Treiberprogrammierer werden? -
Die Frage nach dem "globalen GOTO" hat dir der bladehunter mit longjmp() beantwortet. Das kann wirklich nützlich sein, wenn man weiß, wie man damit umgehen muss.
Das Problem an dieser Funktion ist, dass man ÜBERALL vor ihr gewarnt wird, nicht weil ein globales goto schlecht wäre, sondern weil sie nicht zuverlässig arbeitet.Ja, und deshalb hat Gott den Rückgabewert für Funktionen erfunden. Damit teilt eine "niedere" Funktion ihrem Aufrufer mit, was passiert ist. Die "höhere" aufrufende Funktion kann dann entscheiden, was sie im Fehlerfall unternimmt. Eine Reaktion wäre, sich ihrerseits mit einem entsprechenden Rückgabewert zu beenden und die Fehlerbehandlung einer "noch höheren" Funktion zu überlassen. Das ergibt am Ende das, was du "Durchschleusen der Return-Codes" nanntest.
Exception heißt Ausnahme, dh es sollte auch eher die Ausnahme sein, dass es passiert. Es wird SEHR chaotisch, wenn ich die Fehlerbehandlung über Return-Codes mache, und die Logik meines ganzen Programms auf etwas ausrichte, was nur in Ausnahmefällen passiert.Die Weiterleitung von Exceptions funktioniert übrigens auch nicht anders.
Nein, eben gerade nicht!!
Das Werfen von Exceptions funktioniert wie ein globales goto: Es wird SOFORT alles abgebrochen, wenn eine Exception geworfen wurde und die Kontrolle an den catch Block übergeben, wie man an diesem Code sieht:
#include <cstdlib>
#include <iostream>
using namespace std;
int dividieren(int n, int m)
{
if (m==0)
{
throw 0;
}
return n/m;
}
int f(int n, int m)
{ cout << "Das Ergebnis ist: ";
cout << dividieren(n,m);
cout << ". Alles lief gut. ";
}
int main(int argc, char *argv[])
{ int n, m;
cout << "n = ?n";
cin >> n;
cout << "m = ?n";
cin >> m;
try{
f(n, m);
} catch(int a){
cout << "Ausnahme Nr. " << a << " aufgetreten. ";
system("PAUSE" );
return EXIT_FAILURE;
}
system("PAUSE" );
return EXIT_SUCCESS;
}
Bleibt die Frage, worauf du eigentlich hinauswolltest ...
ich will darauf hinaus, saubere, gut verständliche Programme mit Fehlerbehandlung zu schreiben, was mMn nur mit einem globalem goto möglich ist. Da meine Meinung als Anfänger aber nicht zählt habe ich mal gegooglet und festgestellt, dass es Donald Knuth (und der ist nicht irgendwer) genauso sieht:Sometimes it is necessary to exit from several levels of control, cutting across code that may even have been written by other programmers; and the most graceful way to do this is a direct approach with a go to or its equivalent. Then the intermediate levels of the program can be written under the assumption that nothing will go wrong.
-
DA steht, dass es manchmal nötig ist, Fremdcode zu überspringen, um direkt in eine andere Routine springen zu können.
Ich sehe nur minimale Parallelen zu deinem Problem.
Nebenbei finde ich eine Return Hierarchie wesentlich übersichtlicher, zielgerichteter, intuitiver, [...] als ein einziges Goto Kauderweltsch, was du vor hast.
Aber lassen wir das, du wirst schon wissen was du tust. -
adrians schrieb:
DA steht, dass es manchmal nötig ist, Fremdcode zu überspringen, um direkt in eine andere Routine springen zu können.
Ich sehe nur minimale Parallelen zu deinem Problem.
Nebenbei finde ich eine Return Hierarchie wesentlich übersichtlicher, zielgerichteter, intuitiver, [...] als ein einziges Goto Kauderweltsch, was du vor hast.
Aber lassen wir das, du wirst schon wissen was du tust.
Du hast es NICHT verstanden
Erklär mir doch BITTE mal, warum es try - catch gibt und das auch gerne und oft benutzt wird.
Oder lernt man sowas nicht im Infokurs in der Schule????
warum hat Bjarne Stroustrup in C++ das globale goto für Fehlerbehandlung denn bitte eingeführt?
Ach ja ich weiß, der ist auch wieder blöd und du weißt es besser? Musst ja ein absoluter Profi sein, 30 Jahre Erfahrung oder was, okay, wenn das so ist, werde ich mich natürlich deinem Urteil beugen
Adrians:
Also ehrlich, kein Grund gleich verletzend zu werden. Ich wusste nicht, dass man heutzutage nichtmal sachlich ein Thema erörtern darf oder auf einen Übersetzungsfehler hinweisen darf.
Tut mir wirklich Leid. Ehrlich.
Ich sollte aufhören so ironisch zu sein, oder?
Gut. Viele Smilies machen deine Aussagen auch nicht glaubwürdiger. Irgend ein Zitat in den Raum stellen und sich dann zig tausend Mal darauf berufen ist auch keine Sache.
Ich könnte jetzt behaupten: Bush war 8 Jahre im Amt, der muss es richtig machen. Und? Der Spitzname Ruinator ist nicht von irgendwo vom Himmel gepurzelt und wurde ihm eifnach mal angedichtet.
Nunja, ich verkneife mir den Rest meiner Moralpredigt und wende mich wieder wichtigeren Dingen als dir zu. Dreck oder sowas. :3
Beitrag geändert: 13.11.2008 15:36:44 von adrians -
Donald E. Knuth ist ein Held, okay. Aber der schreibt seine Programme AFAIK in einer theoretischen Maschinensprache für einen nichtexistenten Prozessor mit 256 Registern. Und ich bin mir sicher, dass du noch keines seiner umfangreichen Bücher gelesen hast (die gedruckten Varianten).
Autoritäten zu zitieren hilft dir bei deinem Problem auch nicht weiter.
Erklär mir doch BITTE mal, warum es try - catch gibt und das auch gerne und oft benutzt wird.
Weil es nichts Brauchbareres zum Exception-Handling in Sprachen wie c++ oder Java gibt. Aber es gibt andere Sprachen, die andere Methoden bieten.
Das "gerne und oft benutzt" widerspricht sich übrigen mit deiner vorherigen Aussage:
Exception heißt Ausnahme, dh es sollte auch eher die Ausnahme sein, dass es passiert.
Waaaha, du deklamierst hier wieder etwas, was du dir angelesen hast. Aber hast du es auch verstanden? Wenn ich beispielsweise eine Funktion schreibe, die eine Datei öffnen soll, dann ist das Nichtvorhandensein der Datei oder eine andere Störung, die das Öffnen verhindert eine solche Ausnahme, die zwar selten sein sollte, aber eben auftreten kann. Das signalisiert meine Funktion dem Aufrufer, indem sie keinen gültigen Filehandle zurückgibt, sondern bspw. einen NULL-Pointer.
Es wird SEHR chaotisch, wenn ich die Fehlerbehandlung über Return-Codes mache, und die Logik meines ganzen Programms auf etwas ausrichte, was nur in Ausnahmefällen passiert.
Ich sehe da kein Chaos sondern eine HIERARCHIE: Jede Funktion tut genau das, wozu sie vorgesehen ist. Aber selbstverständlich muss sie alle bei der Ausführung ihrer Aufgabe auftretenden fehlerhaften-Argumente|Ausnahmezustände|Fehler|Whatever abfangen und entsprechende Rückmeldung geben. Nur so ist gewährleistet, dass der Aufrufer diese Funktion sauber benutzen kann. Da der Aufrufer einer Funktion nur über den Rückgabewert den Erfolg erfahren kann (sonst hätte die Funktion Seiteneffekte -- und DIE machen ein Programm chaotisch), muss der Rückgabewert geprüft werden, bei Zeigern macht sich da meist der Vergleich mit NULL gut.
Oder lernt man sowas nicht im Infokurs in der Schule????
Ich hab keinen blassen Schimmer, was man heut zu Tage im InfoKurs in der Schule lernt, aber ich bin mir sicher, dass c (oder gar c++) für Einsteiger ins Programmieren denkbar ungeeignet ist, weil diese Sprache einfach zu viele Spezialregeln und Ausnahmen (und Ausnahmen von den Ausnahmen) enthält, die man erstmal lernen muss um die Sprache selbst zu verstehen, bevor man sich ans Eigentliche, das Programmieren, sprich: Lösen von Problemen durch Aufteilen in Teilprobleme, machen kann. Kleine Übungsaufgabe für dich: Auf wie viele Arten wird das Zeichen "*" verwendet, und wie sind die Vorrangregeln, wenn * auf * trifft?
warum hat Bjarne Stroustrup in C++ das globale goto für Fehlerbehandlung denn bitte eingeführt?
Ach ja ich weiß, der ist auch wieder blöd und du weißt es besser?
Der hat c++ verbrochen und würde gerne den C-Präprozessor abschaffen -- da braucht es keines weiteren Beweises dafür, dass man nicht alles, was der Mann so erfindet und für gut befindet, ernst nehmen muss.
-
Gib mal in eine Suchmaschiene deiner Wahl Programmieren goto ein und dann wirst du bei der überragenden Mehrheit der Treffer feststellen, dass goto(und nahe Verwandte) aus guten Gründen als schlechter Stil angesehen wird.
Und darf ich dich daran erinnern, dass ganze Betriebssysteme in C Programmiert werden? Und zumindest für die OpenSource-Varianten wäre es mir mehr als Neu, wenn dort die Fehlerbehandlungen mit goto o.ä. erfolgen.
http://de.wikipedia.org/wiki/Spaghetti-Code
Und noch etwas ausführlicher:
http://en.wikipedia.org/wiki/Spaghetti_code
#include <cstdlib>
#include <iostream>
using namespace std;
Das ist C++ und nicht C.
int dividieren(int n, int m) { if (m==0) { throw 0; }
Warum nicht return 0?
try{
f(n, m);
} catch(int a){
cout << "Ausnahme Nr. " << a << " aufgetreten. ";
system("PAUSE" );
return EXIT_FAILURE;
}
hm. Du willst bei einer Division durch 0 gleich den Anwender mit der Beendigung des Programms bestrafen?
Also, das kannst du auch ohne Exceptions machen und von überall im Programm aus. Die entsprechende Funktion heißt exit() und ist in der stdlib.h definiert.
Erklär mir doch BITTE mal, warum es try - catch gibt und das auch gerne und oft benutzt wird.
Try-Catch ist eine nette Sache. Das heißt aber nicht, dass man es umbeding in C "nachbauen" muss. Es geht schließlich auch ohne.
warum hat Bjarne Stroustrup in C++ das globale goto für Fehlerbehandlung denn bitte eingeführt?
Dass goto für die Fehlerbehandlung eingeführt wurde, ist mir neu.
Ich denke, goto wurde einfach eingeführt, um die Abwärtskompatibilität zu C zu halten.
Ach ja ich weiß, der ist auch wieder blöd und du weißt es besser? Musst ja ein absoluter Profi sein, 30 Jahre Erfahrung oder was, okay, wenn das so ist, werde ich mich natürlich deinem Urteil beugen
Mich würde wirklich mal interessieren, welches Programmierbuch du hast. Sobald ich das weiß, werde ich mal bei Tante Amazon vorbeischauen und mir die Kommentare durchlesen.
Mit unseren Antworten wollen wir dir keinesfalls auf den Schlips treten. Aber du begehst einen offensichtlichen Fehler und dein Ziel übersichtlichen Code zu schreiben, wirst du durch diese Strategie eben _nicht_ erreichen. -
Autoritäten zu zitieren hilft dir bei deinem Problem auch nicht weiter.
gut, keine Autoritäten aber nur, wenn es dann auch egal ist, dass ich keine Autorität bin. Du brauchst mir nicht in jedem zweiten Satz zu verstehen geben, dass ich keine Ahnung habe.Weil es nichts Brauchbareres zum Exception-Handling in Sprachen wie c++ oder Java gibt. Aber es gibt andere Sprachen, die andere Methoden bieten.
also noch mal: C++ oder Java hier gibt es Exception-Handling, und ich wäre damit 100% zufrieden. Nun programmiere ich leider für ein Gerät, dass C++ nicht mehr schafft (RAM-mäßig).Das "gerne und oft benutzt" widerspricht sich übrigen mit deiner vorherigen Aussage:
natürlich wird es selten benutzt, aber an den Stellen wo es benutzt wird spart es immens viel Aufwand.Waaaha, du deklamierst hier wieder etwas, was du dir angelesen hast.
ich hab mir garnix angelesen, das sind meine eigenen Erfahrungen.Aber hast du es auch verstanden? Wenn ich beispielsweise eine Funktion schreibe, die eine Datei öffnen soll, dann ist das Nichtvorhandensein der Datei oder eine andere Störung, die das Öffnen verhindert eine solche Ausnahme, die zwar selten sein sollte, aber eben auftreten kann. Das signalisiert meine Funktion dem Aufrufer, indem sie keinen gültigen Filehandle zurückgibt, sondern bspw. einen NULL-Pointer.
ja da brauchts einen Genius dafür um das zu verstehen.
Was ist mit den ganz extremen Sachen, wie dass der Speicher ausgeht oder eine Verbindung abbricht, eben was, auf das ich mich nicht vorbereiten kann. Hier läuft es auch nicht linear ab wie bei den Beispielen a) und b) sondern der Fehler tritt uU auf während ein Programmteil arbeitet.Kleine Übungsaufgabe für dich: Auf wie viele Arten wird das Zeichen "*" verwendet, und wie sind die Vorrangregeln, wenn * auf * trifft?
ich mache einfach Klammern und muss mir das nicht merken :P
Gib mal in eine Suchmaschiene deiner Wahl Programmieren goto ein und dann wirst du bei der überragenden Mehrheit der Treffer feststellen, dass goto(und nahe Verwandte) aus guten Gründen als schlechter Stil angesehen wird.
ja, goto wird als schlecht angesehen, aber try - catch IST goto. Und was try - catch angeht sind die Programmierer geteilter Meinung.Und darf ich dich daran erinnern, dass ganze Betriebssysteme in C Programmiert werden? Und zumindest für die OpenSource-Varianten wäre es mir mehr als Neu, wenn dort die Fehlerbehandlungen mit goto o.ä. erfolgen.
Google-Code Suche liefert 1.5 Millionen Treffer für "goto lang:c" und es wird fast immer in Verbindung mit Fehlerbehandlung verwendet. Das gleiche gilt für longjmp - setjmp.
Und wenn die Fehlerbehandlung so aussieht, dass sie nicht existiert...: wenn zB die Platte voll ist, drehen immer noch zu viele Programme einfach durch.Das ist C++ und nicht C.
Ja, darum geht es doch. Iwo oben hat alopex behauptet, dass try - catch nichts anderes ist als das zurückgeben von Return-Codes... naja, der Unterschied ist: wird eine Exception geworfen, wird sofort an den catch-Block übergeben.Warum nicht return 0?
ich hätte dieses Programm so NIEMALS geschrieben, es ist als Beispiel gedacht, um zu sehen, wie try - catch den Programmfluss beeinflussen: wie ein globales goto.
Aber nun zur Praxis: Stell dir ein wirklich kompliziertes Programm vor, was numerische Berechnungen durchführt mit vielen LGS oä und du musst jedesmal Return-Codes zurückgeben, dass auch keine Division durch Null aufgetreten ist (denn sonst gibts einen Runtime-Error und einen Crash)... da wäre doch try-catch wirklich ein Segen, nicht nur weil es einfacher wird, sondern weil sehr wahrscheinlich auch schneller wird, überleg dir mal wie viele Abfragen du dir sparst.Dass goto für die Fehlerbehandlung eingeführt wurde, ist mir neu.
ich meinte damit try - catch, das benutze ich synonym zu "globales goto", weils mMn wirklich exakt das selbe ist.
Ich denke, goto wurde einfach eingeführt, um die Abwärtskompatibilität zu C zu halten.Mit unseren Antworten wollen wir dir keinesfalls auf den Schlips treten. Aber du begehst einen offensichtlichen Fehler und dein Ziel übersichtlichen Code zu schreiben, wirst du durch diese Strategie eben _nicht_ erreichen.
offensichtlich? imho fühle ich mich zumindest teilweise dadurch bestätigt, dass es sowas wie Interrupts oder Exceptions gibt. Das ist das Äquivalent von rumspringen im Code, manchmal ist das eben unvermeidlich. Das letzte System was ich hatte, basierte auf Forth und das hatte ein Konstrukt TRY - RECOVER - ENDTRY, auf das man sich 100%-ig verlassen konnte.
-
Autoritäten zu zitieren hilft dir bei deinem Problem auch nicht weiter.
gut, keine Autoritäten aber nur, wenn es dann auch egal ist, dass ich keine Autorität bin. Du brauchst mir nicht in jedem zweiten Satz zu verstehen geben, dass ich keine Ahnung habe.
Das hat Niemand behauptet. Dein Problem ist einfach, dass du C anders benutzen willst, als es eigentlich vorgesehen ist.
Daher frage ich mich, welche Sprache du vorher benutzt hast. Basic vielleicht?
Und ich gebe Donald Knuth vollkommen Recht, dass goto eine Darseinsberechtigung hat. Allerdings nur für einige, seltene Fälle.
Wie bereits in meinem 1. Post angemerkt, muss man sich vorher genau überlegen, ob goto wirklich sinnvoll ist.
Weil es nichts Brauchbareres zum Exception-Handling in Sprachen wie c++ oder Java gibt. Aber es gibt andere Sprachen, die andere Methoden bieten.
also noch mal: C++ oder Java hier gibt es Exception-Handling, und ich wäre damit 100% zufrieden. Nun programmiere ich leider für ein Gerät, dass C++ nicht mehr schafft (RAM-mäßig).
Musst du den Sourcecode auf dem Gerät selber compilieren?
Eigentlich sollte compilierter C++ Code (einen vernünftigen Compiler vorrausgesetzt) keinen alzu großen Leistungsunterschied zu "compiliertem" Assembler-Code haben.
Ansonten besteht noch die Möglichkeit - und das meine ich nicht als Beleidigung - dass deine Fähigkeiten in C++ nicht so gut sind bzw. du deinen Code so schreibst, dass er eher den Stil einer anderen Programmiersprache als C++ entspricht.
Das "gerne und oft benutzt" widerspricht sich übrigen mit deiner vorherigen Aussage:
natürlich wird es selten benutzt, aber an den Stellen wo es benutzt wird spart es immens viel Aufwand.
So wie ich dich verstanden habe, willst du aber für jede Fehlerbehandlung goto benutzen.
Waaaha, du deklamierst hier wieder etwas, was du dir angelesen hast.
ich hab mir garnix angelesen, das sind meine eigenen Erfahrungen.
In der Programmiersprache C oder C++ ?
[...]Das signalisiert meine Funktion dem Aufrufer, indem sie keinen gültigen Filehandle zurückgibt, sondern bspw. einen NULL-Pointer.
ja da brauchts einen Genius dafür um das zu verstehen.
Du weißt also nicht, was ein NULL-Pointer ist?
Oder meinst du, dass man den Sourcecode, der mit solchen Rückgabewerten arbeitet, nur schwer verstehen kann? Dass man NULL-Pointer im Fehlerfall zurückgibt ist eigentlich schon Konvention und sollte einen nicht zu sehr überraschen.
Was ist mit den ganz extremen Sachen, wie dass der Speicher ausgeht oder eine Verbindung abbricht, eben was, auf das ich mich nicht vorbereiten kann. Hier läuft es auch nicht linear ab wie bei den Beispielen a) und b) sondern der Fehler tritt uU auf während ein Programmteil arbeitet.
Bei einem vollem Speicher bekommst du einen NULL-Pointer zurückgeliefert. Daran kannst du dann ganz einfach sehen, dass du keinen Platz mehr hast.
Und betreffend Netzwerkverbindungen:
http://zotteljedi.de/doc/socket-tipps/recv.html
Kehrt recv() zurück, aber hat 0 Bytes gelesen, wurde der Socket auf der Gegenseite geschlossen.
Also macht man einfach ein
while(recv(...)){tu was}
Und gut ist, denn, wenn die Verbindung geschlossen wurde, gibt recv den Wert 0 zurück und dieser ist = false und somit beendet sich die Schleife und der Code hinter while(){} kann dann aufräumen.
Kleine Übungsaufgabe für dich: Auf wie viele Arten wird das Zeichen "*" verwendet, und wie sind die Vorrangregeln, wenn * auf * trifft?
ich mache einfach Klammern und muss mir das nicht merken
Nachschauen tut auch nicht weh ;)
http://www.cppreference.com/wiki/operator_precedence
Gib mal in eine Suchmaschiene deiner Wahl Programmieren goto ein und dann wirst du bei der überragenden Mehrheit der Treffer feststellen, dass goto(und nahe Verwandte) aus guten Gründen als schlechter Stil angesehen wird.
ja, goto wird als schlecht angesehen, aber try - catch IST goto. Und was try - catch angeht sind die Programmierer geteilter Meinung.
Wenn try-catch = goto ist, warum hat man dann try-catch überhaupt eingeführt?
Also, ganz Unrecht hast du nicht. Da es in der Maschienensprache nur goto (bzw. jmp) gibt und der ganze Code eben in diese Maschienensprache umgewandelt wird.
Aber Hochsprachen wie C/C++ sind nunmal dazu da, damit man eben nicht mehr die ganze Zeit mit goto rumhantieren muss. Diese Sprachen verfügen über die praktischen Konstrukte if,else,for,while,switch,try, ect. um die Übersichtlichkeit zu erhöhen.
Und in C kannst du eben immer über den Rückgabewert herausfinden, ob etwas schief gelaufen ist. Falls du dann eine komplizierte Fehlerbehandlung brauchst, kannst du auch einfach eine extra-Funktion schreiben und die entsprechenden Variablen als Referenzen übergeben, damit die behandelnde Funktion das ganze fixen kann.
Und darf ich dich daran erinnern, dass ganze Betriebssysteme in C Programmiert werden? Und zumindest für die OpenSource-Varianten wäre es mir mehr als Neu, wenn dort die Fehlerbehandlungen mit goto o.ä. erfolgen.
Google-Code Suche liefert 1.5 Millionen Treffer für "goto lang:c" und es wird fast immer in Verbindung mit Fehlerbehandlung verwendet. Das gleiche gilt für longjmp - setjmp.
Komisch. Ich kriege genau das Gegenteilige Ergebnis.
Und bei den paar Treffern, wo longjmp wirklich im Zusammenhang zur Fehlerbehandlung genannt wird, wird auch direkt und ausdrücklich darauf hingewiesen, dass man das vermeiden sollte.
Und longjmp kann unter Umständen die Stack-Reihenfolge durcheinander bringen, wenn du nicht aufpasst. Und solche Fehler sind dann verdammt schwer zu finden.
Und wenn die Fehlerbehandlung so aussieht, dass sie nicht existiert...: wenn zB die Platte voll ist, drehen immer noch zu viele Programme einfach durch.
Fehlerbehandlung ist deswegen auch sehr sinnvoll. Und es ist sinnvoll dies über Funktionen und Rückgabewerte zu machen.
Das ist C++ und nicht C.
Ja, darum geht es doch. Iwo oben hat alopex behauptet, dass try - catch nichts anderes ist als das zurückgeben von Return-Codes... naja, der Unterschied ist: wird eine Exception geworfen, wird sofort an den catch-Block übergeben.
Du musst Exceptions aber genauso nach "oben" weiterreichen, wie du es mit Funktionsrückgabewerten tust.
Warum nicht return 0?
ich hätte dieses Programm so NIEMALS geschrieben, es ist als Beispiel gedacht, um zu sehen, wie try - catch den Programmfluss beeinflussen: wie ein globales goto.
hm. Dann könnte ich auch sagen, dass ein Funktionsaufruf ein globales goto ist.
Aber nun zur Praxis: Stell dir ein wirklich kompliziertes Programm vor, was numerische Berechnungen durchführt mit vielen LGS oä und du musst jedesmal Return-Codes zurückgeben, dass auch keine Division durch Null aufgetreten ist (denn sonst gibts einen Runtime-Error und einen Crash)... da wäre doch try-catch wirklich ein Segen, nicht nur weil es einfacher wird, sondern weil sehr wahrscheinlich auch schneller wird, überleg dir mal wie viele Abfragen du dir sparst.
Die Länge des Codes spiegelt nicht zwangsläufig die Qualität/Geschwindigkeit des Codes wieder. Rekursion lässt grüßen
Bei einem linearem Gleichungssystem müsstest du einen Vektor als Rückgabewert verwenden. Dies ist kein primitiver Datentyp und daher wirst du einen Pointer auf diesen Rückgabewert zurückgeben.
Wenn du jetzt irgendwo kurz vor eine Division stehst, kannst du einfach eine if-Abfrage reinbauen, ob der Nenner 0 ist und dann einen Fehlertext ausgeben und einen NULL-Pointer zurückgeben.
Dass goto für die Fehlerbehandlung eingeführt wurde, ist mir neu.
Ich denke, goto wurde einfach eingeführt, um die Abwärtskompatibilität zu C zu halten.
ich meinte damit try - catch, das benutze ich synonym zu "globales goto", weils mMn wirklich exakt das selbe ist.
Nein? Du musst schließlich das ganze so notieren:
try{ may_fail(); } catch(die_exeption_so_und_so) { aufraeum(); }
Das ganze ist ein Stück Code, dass du nicht auseinanderreißen kannst. Folglich ist es auch nicht global.
Mit unseren Antworten wollen wir dir keinesfalls auf den Schlips treten. Aber du begehst einen offensichtlichen Fehler und dein Ziel übersichtlichen Code zu schreiben, wirst du durch diese Strategie eben _nicht_ erreichen.
offensichtlich? imho fühle ich mich zumindest teilweise dadurch bestätigt, dass es sowas wie Interrupts oder Exceptions gibt. Das ist das Äquivalent von rumspringen im Code, manchmal ist das eben unvermeidlich. Das letzte System was ich hatte, basierte auf Forth und das hatte ein Konstrukt TRY - RECOVER - ENDTRY, auf das man sich 100%-ig verlassen konnte.
Forth kennt also Interrupts? Interessant zu wissen. Ich dachte, das wäre Assembler vorbehalten.
Du solltest aufhören C so zu behandeln, als wäre es eine andere Sprache. Du wirst dich damit abfinden müssen, dass C keine Exceptions bietet. Und du solltest einsehen, dass es auch ohne geht. -
Also macht man einfach ein
Siehst du??? DU kommst selber damit nicht klar.
Code:
1:
while(recv(...)){tu was}
Und gut ist, denn, wenn die Verbindung geschlossen wurde, gibt recv den Wert 0 zurück und dieser ist = false und somit beendet sich die Schleife und der Code hinter while(){} kann dann aufräumen.
Du solltest aufhören C so zu behandeln, als wäre es eine andere Sprache. Du wirst dich damit abfinden müssen, dass C keine Exceptions bietet. Und du solltest einsehen, dass es auch ohne geht.
das habe ich NIEMALS in Frage gestellt, dass es auch ohne Exceptions geht. Aber an deinem eigenen Beispielcode siehst du es doch am Besten, wo das Problem liegt.
- du musst checken ob recv(...) == 0 ist
- du musst checken ob recv(...) == -1 ist
- du musst checken ob recv(...) < Buffer-Größe ist
du musst einen Buffer (Chunk) anlegen, und der darf nicht zu groß sein (weil sonst nicht schnell genug abgebrochen wird) und nicht zu klein sein, weil natürlich jedes Aufrufen von recv( ) Zeit kostet. Gäbs ne Exception könnte man einfach einen großen Buffer anlegen und recv(...) mal machen lassen.
Ich denke es hat sich jetzt erledigt, das Thema. Es geht NICHT und ich mache es halt OHNE. -
Siehst du??? DU kommst selber damit nicht klar.
Was meinst du genau?
das habe ich NIEMALS in Frage gestellt, dass es auch ohne Exceptions geht. Aber an deinem eigenen Beispielcode siehst du es doch am Besten, wo das Problem liegt.
Ganz im Gegenteil: Ich sehe kein Problem.
- du musst checken ob recv(...) == 0 ist
Richtig. Dafür benutze ich dann eine while-Schleife und gut ist.
Eine Exception wäre sowieso fehl am Platz, da jede Verbindung irgendwann geschlossen wird.
- du musst checken ob recv(...) == -1 ist
Nein. Dieser Fall kann bei dieser Funktion meines Wissens nach nicht auftreten.
- du musst checken ob recv(...) < Buffer-Größe ist
Nein, weil der 3. Parameter len festlegt, wie groß der Buffer ist.
du musst einen Buffer (Chunk) anlegen, und der darf nicht zu groß sein (weil sonst nicht schnell genug abgebrochen wird)
Was meinst du mit "schnell genug abgebrochen"?
und nicht zu klein sein, weil natürlich jedes Aufrufen von recv( ) Zeit kostet.
Klar.
Gäbs ne Exception könnte man einfach einen großen Buffer anlegen und recv(...) mal machen lassen.
hm. Und was würde diese Exception dann machen? -
Sorry, wenn ich deine Fähigkeiten aufs Gröbste unterschätzt habe. Es gab da so Anzeichen, wie c und c++ durcheinanderwerfen, "unbedingt ein GOTO haben wollen", "Maschine mit ie" und du hast dich ja auch als Anfänger bezeichnet -- dass sich dies nur auf die Erfahrung mit c|c++ bezieht, war nicht von Anfang an ersichtlich ...
Möglicherweise hätte eine konkretere Problembeschreibung deinerseits dieses Missverständnis schneller ausgeräumt. Leider bist du immer noch nicht konkreter geworden, daher kann ich nur vermuten, dass du c++-Exceptions in nacktem c nachbauen willst|möchtest|musst. Mögliche Lösungswege findest hinter den zwei Links weiter unten in diesem Post.
Ich bin so beim Rum-Googlen noch auf eine interessante Antwort auf eine deiner Fragen gestoßen:
Erklär mir doch BITTE mal, warum es try - catch gibt und das auch gerne und oft benutzt wird.
I think the reason programmers in C/C++/Java style languages have been attracted to exceptions is simply because the syntax does not have a concise way to call a function that returns multiple values, so it's hard to write a function that either produces a return value or returns an error.
Das schrieb Joel Spolsky schon 2003 und da du Autoritätsbeweise so magst ...
http://www.joelonsoftware.com/items/2003/10/13.html
Also, ganz Unrecht hast du nicht. Da es in der Maschienensprache nur goto (bzw. jmp) gibt und der ganze Code eben in diese Maschienensprache umgewandelt wird.
Ich bin kein sehr guter Kenner von Assembler|Maschinensprache. Aber alle Prozessoren, die mir bisher untergekommen sind, bieten Subroutinenaufrufe mit CALL oder JSR, und eine Rückkehr zu der aufrufenden Stelle im Programmablauf mit RET (sprich, dass was in C Funktionen sind) -- nur für das Retten der Register muss man selbst sorgen.
Aber Hochsprachen wie C/C++ sind nunmal dazu da, damit man eben nicht mehr die ganze Zeit mit goto rumhantieren muss. Diese Sprachen verfügen über die praktischen Konstrukte if,else,for,while,switch,try, ect. um die Übersichtlichkeit zu erhöhen.
... und selbstverständlich verfügen diese Prozessoren auch über Verzweigungsanweisungen (if-else), nur sind dass dann eben bedingte Sprünge (in dem Fall also "conditional gotos" ). Schleifen mit Zählvariablen gibts auch: LDIR, LDDR würden mir da beispielsweise einfallen. Und Exception-Handling hat jeder vernünftige Prozessor serienmäßig eingebaut, nur dass die Exceptions eben bei manchen Herstellern Traps und bei anderen wieder (Software-)Interrupts heißen. Aber das erwähnte ich ja schon. Wie die Fähigkeiten einer echten Hochsprache dann auf die Prozessor-Sprache abgebildet werden, ist aber Sache des Compilers und kann dem Programmierer in der Hochsprache am Allerwertesten vorbeigehen. Aber im Prinzip sind alle Hochsprachkonstrukte auch in Assembler|Maschinensprache machbar.
also noch mal: C++ oder Java hier gibt es Exception-Handling, und ich wäre damit 100% zufrieden. Nun programmiere ich leider für ein Gerät, dass C++ nicht mehr schafft (RAM-mäßig).
- Sagt dir "CrossCompiling" was? Man muss ein Programm ja nicht auf der Zielmaschine kompilieren.
- Es gibt auch JavaUmgebungen für leistungsschwächere Maschinen: JavaME wäre da ein Beispiel.
warum hat Bjarne Stroustrup in C++ das globale goto für Fehlerbehandlung denn bitte eingeführt?
Global hört sich für mich aber anders an als, "In C, goto only works within the scope of the current function ...". (nein das Zitat ist nicht von B.S.). Ein solches "lokales" GOTO, das nur Labels innerhalb einer Funktion anspringen könnte, würde dir dann aber auch nicht weiterhelfen, oder?
Ich denke es hat sich jetzt erledigt, das Thema. Es geht NICHT und ich mache es halt OHNE.
Wer wird denn gleich aufgeben?
http://www.ddj.com/cpp/184401349
http://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-40.pdf
Dessen TRY-EXCEPT-ENDTRY sollte deinen Forth-Erfahrungen doch schon ziemlich nahe kommen, nutzt aber setjmp() und longjmp().
So wie es ausschaut, beruhen beide Lösungen auf Präprozessortricks. Und das beweist mal wieder, dass c eine ziemlich armselige Sprache wäre, ohne den Präprozessor.
P.S.:
http://xkcd.com/163/ (knuth)
http://xkcd.com/292/ (goto)
Beitrag geändert: 10.11.2008 12:32:17 von alopex -
Es ist wohl möglich Exception Handling in C sich selber zu bastlen.
Dabei muss man setjmp benutzen
BESCHREIBUNG setjmp und longjmp(3) sind nützlich für die Behandlung von Fehlern und Unterbrechungen, die in einem low-level-Unterprogramm eines Programms auftreten. setjmp() sichert den Stack-Zusammenhang bzw. die Stack- Umgebung in env für spätere Benutzung durch longjmp(). Der Stack- Zusammenhang wird ungültig wenn die Funktion, die setjmp() aufgerufen hat, beendet wird.
Ansonsten sehe ich keinen Weg, außer RETURN werte oder eine globale Error Variable, welche wie errno oder GetLastError() ( WINAPI ) benutzt wird. -
Richtig. Dafür benutze ich dann eine while-Schleife und gut ist.
Warum wird in dem Codebeispiel von deinem Link dann auf den Rückgabewert -1 überprüft? Anscheinend bedeutet der Rückgabewert -1, dass ein Fehler aufgetreten ist, 0, dass die Verbindung geschlossen wurde.
Eine Exception wäre sowieso fehl am Platz, da jede Verbindung irgendwann geschlossen wird.Nein, weil der 3. Parameter len festlegt, wie groß der Buffer ist.
was ist denn, wenn die Verbindung geschlossen wurde, BEVOR der Puffer ganz gefüllt wurde???
Die Anzahl der übertragenen Bytes müssen ja nicht ein Vielfaches der Pufferlänge sein.
Was meinst du mit "schnell genug abgebrochen"?
also ich habe keine Ahnung wie recv() so angewendet wird, wahrscheinlich ist daher die Überlegung in der Praxis unwichtig, aber so sind maximal alle Daten von der Größe des Buffers Müll, denn im Fehlerfall gibt sie -1 statt der Anzahl der übertragenen Daten zurück. "Abbrechen" selbst tut es natürlich schnell, aber der Rest der Funktion merkt es zu spät, mit einer Exception wäre das anders.
Möglicherweise hätte eine konkretere Problembeschreibung deinerseits dieses Missverständnis schneller ausgeräumt. Leider bist du immer noch nicht konkreter geworden, daher kann ich nur vermuten, dass du c++-Exceptions in nacktem c nachbauen willst|möchtest|musst.
ähm, ja, sorry. Genau das wollte ich."Maschine mit ie"
das war ich nicht, das war bladehunterGlobal hört sich für mich aber anders an als, "In C, goto only works within the scope of the current function ...". (nein das Zitat ist nicht von B.S.). Ein solches "lokales" GOTO, das nur Labels innerhalb einer Funktion anspringen könnte, würde dir dann aber auch nicht weiterhelfen, oder?
auch hier hab ich mich ungenau ausgedrückt. Genau, es gibt ein lokales goto in C und damit auch in C++ und das hilft mir auch tatsächlich nicht weiter.
Mit goto hab ich try-catch gemeint, weil sich try-catch theoretisch dazu nutzen lässt eine globale goto Funktion wie in Basic einzuführen, man müsste nur die main Prozedur mit try-catch einrahmen, und im catch-Block die Funktionen angeben zu denen man will (je nach Art der Exception), daher: try-catch + Funktionsaufruf ergeben mMn sowas wie ein globales goto.
ich werd es bei Gelegenheit noch mal probieren mit C++ probieren. Offiziell unterstützt ist leider NUR C (gcc) mit ner speziellen, wohl etwas abgespeckten Standardbibliothek.
- Sagt dir "CrossCompiling" was? Man muss ein Programm ja nicht auf der Zielmaschine kompilieren.
- Es gibt auch JavaUmgebungen für leistungsschwächere Maschinen: JavaME wäre da ein Beispiel.Wer wird denn gleich aufgeben?
Vielen Dank für diese Links. Ich werd mich nach dem ersten orientieren, ich hab Angst, dass ich von nem Dino gefressen werde, wenn ich longjmp benutze
http://www.ddj.com/cpp/184401349
http://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-40.pdf
Dessen TRY-EXCEPT-ENDTRY sollte deinen Forth-Erfahrungen doch schon ziemlich nahe kommen, nutzt aber setjmp() und longjmp().
So wie es ausschaut, beruhen beide Lösungen auf Präprozessortricks. Und das beweist mal wieder, dass c eine ziemlich armselige Sprache wäre, ohne den Präprozessor.
Zwei Sachen muss ich noch los werden...
Nachschauen tut auch nicht weh ;)
es gibt nur eine Vorrangregel, die man wissen muss, und die heißt Punkt vor Strich alle anderen WILL ich gar nicht wissen, dass Addition vor Right-Shift kommt oä WILL ich nicht wissen
http://www.cppreference.com/wiki/operator_precedenceForth kennt also Interrupts? Interessant zu wissen. Ich dachte, das wäre Assembler vorbehalten.
Ja!! Mit ASSIGN kannst du einem Wert in der Interrupt-Tabelle ein Forth-Wort zuweisen. Wenn die Unterbrechung eintritt, wird das Wort ausgeführt. Das geht natürlich nur wenn die Forth-Umgebung die totale Kontrolle über die Hardware hat. Also mit nem Forth für Windows geht so was nicht. -
@Alopex: Keine Lust, das jetzt alles nochmal zu zitieren. Du hast in allen Punkten betreffend meiner Posts Recht.
Aber die Befehle, die du genannt hast, stammen nicht alle aus dem x86 Instruction Set, oder?
Richtig. Dafür benutze ich dann eine while-Schleife und gut ist.
Eine Exception wäre sowieso fehl am Platz, da jede Verbindung irgendwann geschlossen wird.
Warum wird in dem Codebeispiel von deinem Link dann auf den Rückgabewert -1 überprüft? Anscheinend bedeutet der Rückgabewert -1, dass ein Fehler aufgetreten ist, 0, dass die Verbindung geschlossen wurde.
Da hast du wahrscheinlich Recht. Ich bin davon ausgegangen, dass der Fehlerfalls wie eine geschlossene Verbindung behandelt wird.
Nein, weil der 3. Parameter len festlegt, wie groß der Buffer ist.
was ist denn, wenn die Verbindung geschlossen wurde, BEVOR der Puffer ganz gefüllt wurde???
Die Anzahl der übertragenen Bytes müssen ja nicht ein Vielfaches der Pufferlänge sein.
Der Buffer muss nicht vollständig gefüllt werden. Prinzipiell kannst du davon ausgehen, dass jedes Packet, dass über die Leitung geht, verschieden groß ist. Dein Buffer hat jedoch eine feste Größe, die alle angenommenen Packete nutzen müssen.
Und wenn ich mich nicht irre, dann wird immer nur 1 Packet in den Buffer geschrieben, irgendwas tolles mit den Inhalten des Buffers gemacht und dann nochmal recv() aufgerufen und der Buffer neu befüllt mit den Inhalten des nächsten Packets.
Was meinst du mit "schnell genug abgebrochen"?
also ich habe keine Ahnung wie recv() so angewendet wird, wahrscheinlich ist daher die Überlegung in der Praxis unwichtig, aber so sind maximal alle Daten von der Größe des Buffers Müll, denn im Fehlerfall gibt sie -1 statt der Anzahl der übertragenen Daten zurück. "Abbrechen" selbst tut es natürlich schnell, aber der Rest der Funktion merkt es zu spät, mit einer Exception wäre das anders.
Du denkst, dass alle Packete direkt in den Buffer geschrieben werden, wenn sie übers Netzwerk ankommen?
Das ist nicht so. Die Packete werden zunächst vom Betriebssystem in Empfang genommen und einem Socket zugeordnet.
Die Funktion recv() holt sich dann 1 Packet vom Betriebssystem ab.
Und wenn man recv() aufruft und -1 zurückgegeben wird, dann sollten eigentlich sowieso keine neuen Daten in den buffer geschrieben worden sein.
"Maschine mit ie"
das war ich nicht, das war bladehunter ;)
Richtig. Schlechte Angewohnheit ;)
es gibt nur eine Vorrangregel, die man wissen muss, und die heißt Punkt vor Strich alle anderen WILL ich gar nicht wissen, dass Addition vor Right-Shift kommt oä WILL ich nicht wissen :D
Könnte trotzdem sein, dass du über fremden Code stolperst, der sich eben diese Regeln zunutze macht.
Und zu viele Klammern sind auch nicht schön ;) -
@Alopex: Keine Lust, das jetzt alles nochmal zu zitieren. Du hast in allen Punkten betreffend meiner Posts Recht.
Du machst mir Angst.
Aber die Befehle, die du genannt hast, stammen nicht alle aus dem x86 Instruction Set, oder?
Nö, die "Schleifen"-Befehle gabs schon im 8080-Befehlssatz. Der Prozessor ist zwar auch von Intel, hatte aber lange vor dem ersten 86-er seine große Zeit.
"Maschine mit ie"
das war ich nicht, das war bladehunter ;)
Richtig. Schlechte Angewohnheit ;)
Bin ich beim Quotieren durcheinandergekommen (wie so oft) -- sorry.
@D.M.:
es gibt nur eine Vorrangregel, die man wissen muss, und die heißt Punkt vor Strich alle anderen WILL ich gar nicht wissen, dass Addition vor Right-Shift kommt oä WILL ich nicht wissen :D
Da sind wir uns wohl fast einig. Ich finde es gruselig, eine Latte von Vorrangregeln zu lernen, wenn man Klammern setzen und ungeklammerte Ausdrücke einfach links-nach-rechts abarbeiten könnte. Meiner völlig unbedeutenden Meinung nach könnte man sogar das mathematische Punkt-vor-Strich weglassen. Einzig bei (boolean) UND vor ODER könnte ich mich erweichen lassen.
@bladehunter:
Könnte trotzdem sein, dass du über fremden Code stolperst, der sich eben diese Regeln zunutze macht.
Das Problem dabei ist, man kann sich nie sicher sein, ob der Schreiber des fremden Codes auch alle Regeln kannte, oder der Programm-Teil nur aus Versehen so funktioniert wie beabsichtigt.
Und zu viele Klammern sind auch nicht schön ;)
LISP-Code ist vielleicht nicht schön, dafür soll die Sprache aber sehr leistungsfähig sein. Und FORTH ist ja ein relativ enger Verwandter davon.
Beitrag geändert: 15.11.2008 17:40:04 von alopex -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage