Hello World In Cee
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
Selbst darüber, wie eigentlich das erste C-Programm aussehen sollte, kann es verschiedene Meinungen geben. Der Klassiker
ist wohl
| #include <stdio.h>
int main() {
printf("Hello World\n");
return 0;
} |
|
|
ChristianDühl schlägt vor:
| #include <stdio.h>
int main(void)
{
puts("Hallo Welt");
return 0;
} |
|
|
Diskussion:
Einmal davon abgesehen, daß der Klassiker die EinzigWahreArtGeschweifteKlammernZuSetzen verwendet, finde ich interessant, dass der Gegenvorschlag zur Ausgabe einer Zeichenkette mit anschließendem Zeilenvorschub die geeignetste Funktion verwendet, die die Standardbibliothek von C dazu zu bieten hat. Warum sollte man Einsteiger auf den Irrweg bringen, dass das Schwergewicht printf() die Ausgabemethode in C ist? Für den Klassiker sprechen historische Gründe. Für ein erstes Programm in C ist aber der Vorschlag von ChristianDühl besser geeignet, weil es möglich ist, die Aufgabe der Funktion puts() in wenigen Worten darzustellen und bei dieser Darstellung auch zu bleiben. -- KurtWatzka
Darüberhinaus ist das fehlende void als Parameter von main() IIRC erst in C++ erlaubt. Und über die EinzigWahreArtGeschweifteKlammernZuSetzen kann man getrost streiten :-) -- ChristianDühl
- Bei der Definition einer Funktion in C bedeutet seit mehr als 10 Jahren ein Paar leerer Klammern, dass die Funktion keine Parameter hat. Das ist keineswegs erst in C++ erlaubt. Vielleicht liegt da eine Verwechslung mit der Deklaration einer Funktion in C vor, wo leere Klammern zwar ebenfalls erlaubt sind, aber eine andere Bedeutung haben als in C++, nämlich die Bedeutung: "Die Anzahl der Parameter und deren Typ sind nicht spezifiziert, aber fest" -- kw
Man beachte auch die EinzigWahreEinrückTiefe ;), die ich jetzt bei meinem Vorschlag vorgenommen habe. -- ChristianDühl
Vielleicht sollte man sich auch darüber klar werden, ob es sich eigentlich um ein "Hello World"-Programm für C89 oder für C99 handeln soll. Schon für "Hello World" ergeben sich da Unterschiede:
- Implizites "return 0" am Ende von main in C99
- In C89 kann puts() ohne Prototyp aufgerufen werden, weil der angenommene Typ einer Funktion ohne Prototyp mit dem tatsächlichen Typ von puts() übereinstimmt. Die implizite Deklaration einer Funktion ist in C99 weggefallen. -- Jedoch ist das immer eine schlechte Idee und vor allem für einen Neuling irreführend, man erinnere nur an das falsche Verwenden von malloc() ohne vorherige richtige Deklaration. Solch ein Vorgehen beim Erlernen einer Programmiersprache führt meines Erachtens zu einem schlechten Stil beim später professionellen Programmierer. -- ZoranCutura
- Ziel war auch eher, die Unterschiede zwischen einem ganz einfachen Programm in C89 und C99 darzustellen. Ich denke es ist keine Hintertür, sondern etwas recht nützliches, zu wissen, welche Annahmen ein C89-Compiler macht, wenn er auf einen Aufruf einer noch nicht deklarierten Funktion stößt. Wer weiss, dass die Funktion dann so behandelt wird, als ob sie den Rückgabetyp int hätte und eine feste Anzahl von Parametern erwarten würde, dem wird auch klar, warum ein Aufruf von printf() oder malloc() ohne Deklaration ein Fehler, ein Aufruf von puts() ohne Deklaration dagegen kein Fehler ist. Früher oder später wird ein Programmierer ja doch darauf kommen, dass es manchmal funktioniert, wenn eine Funktion ohne Deklaration aufgerufen wird, in anderen Fällen aber wieder nicht. Warum soll geheimgehalten werden, warum das so ist? Jedenfalls ist das ein Unterschied zwischen C89 und C99. Und nein, ich glaube nicht, dass genaueres Wissen um eine Spracheigenschaft zu einem schlechten Stil führt. -- kw
- In C89 gab es implizites int, das ist in C99 weggefallen. Man sollte wirklich nicht alle Hintertüren verwenden die einem offen stehen. :-) Obige Anmerkung gilt auch hier. -- ZoranCutura
- Kein "implicit int" ist ein Unterschied zwischen C89 und C99. Diesen Unterschied läßt sich meiner Meinung nach am leichtesten dadurch demonstrieren, dass man es in C89 verwendet. -- kw
- Das ist alles sehr richtig aber meiner Meinung nach für das berühmte Hello World nicht empfehlenswert. Hello World sollte als die lehrende Erstreferenz gesehen werden und ist an dieser Stelle wohl auch als eine Referenz für einen zukünftig sauberen Stil zweckmäßig. Trotzdem ist es wichtig, die Ecken und Kanten der Programmiersprache zu kennen, aber eben erst mit der Erfahrung und nicht mit dem ersten Programm, welches man schreibt. Da finde ich Christians Vorschlag den korrektesten. -- zc
- Wer diese Seite aufmerksam liest, dem sollte klar sein, dass auch ich als "Hello, World" Programm in dem Sinne, wie es in einem Lehrbuch verwendet wird, den Vorschlag von ChristianDühl besser finde als den "Klassiker". Für eine Demonstration von Unterschieden zwischen C89 und C99 halte ich aber die beiden untenstehenden Programme für geeignet - einschließlich einer detaillierten Erläuterung warum das eine ein korrektes C89-Programm, das andere ein korrektes C99-Programm ist. -- kw
- Ich denke wir sollten uns darüber klar werden, was wir mit den HelloWorld Programmen bezwecken wollen. Beides wäre ein "sinnvoller" Ansatz und mit den hier vorgestellten (und dann hoffentlich gut dokumentierten) C89/C99-Versionen könnte ich auch gut leben :) -- cd
Vorschlag (nicht direkt unter HelloWorld):
Einige Unterschiede zwischen C89 und C99, vorgestellt am Beispiel "Hello World" | |
C89:
| main()
{
puts("Hallo Welt");
return 0;
} |
|
|
Dies ist ein gültiges C89-Programm, weil
- Der Rückgabetyp einer Funktion int ist, wenn kein Rückgabetyp angegeben wird so dass main den richtigen Rückgabetyp und eine der beiden auch in einem "hosted environment" zugelassenen Signaturen für main hat.
- Der Compiler annimmt, dass eine Funktion, die aufgerufen wird, vor sie durch einen Prototyp eingeführt wurde, den Rückgabetyp int hat und eine feste Anzahl von Parametern erwartet. Beides trifft auf die Funktion puts() aus der Standardbibliothek zu. Auch der Typ des aktuellen Parameters (Zeiger auf char) ist mit dem von puts() erwarteten formalen Parameter verträglich.
Dies ist kein gültiges C99-Programm, weil
- Der Rückgabetyp der Funktion main nicht angegeben ist
- Die Funktion puts() aufgerufen wird, ohne dass sie vorher deklariert oder definiert wurde
C99:
| #include <stdio.h>
int main()
{
puts("Hallo Welt");
} |
|
|
Dies ist ein gültiges C99-Programm, weil
- die Ausführung die schließende Klammer der zusammengesetzten Anweisung erreicht, die den Rumpf der Funktion main darstellt, ohne dass ein return-Statement ausgeführt wird. In C99 wird dann nur für die Funktion main ein implizites "return 0" eingeschoben.
Es ist außerdem ein gültiges C89-Programm, das aber einen undefinierten Status zurückliefert. Nicht das Verhalten des Programms, sondern nur der Status ist undefiniert. Wird der Status verwendet, so ist auch das Verhalten undefiniert. (Diskussionspunkt)
-- kw
Vorschlag (für HelloWorld)
wie von ChristianDühl vorgeschlagen, aber möglicherweise ohne void in den Klammern.
- Die Definition von main ist nur nur dann sinnvollerweise ein Prototyp, wenn main auch wirklich rekursiv aufgerufen werden soll. Für main ist die Definition, die auch ein Prototyp ist, also außerhalb des ObfuscatedCee?-Wettbewerbs nicht sinnvoll.
- Eine Definition von main mit leerer Parameterliste beugt dem Irrglaube vor, dass eine leere Parameterliste "erst in C++ erlaubt" sei.
- Für jede Funktion die keine Parameter erwartet ist es sinnvoll, eine Definition zu schreiben, die auch ein Prototyp ist:
- Wenn die Funktion vor ihren ersten Aufruf geschrieben werden kann, dann ist die Definition gleichzeitig auch Prototyp, und damit entfällt die Notwendigkeit einer getrennten Prototyp-Deklaration
- Wenn ein Aufruf der Funktion eine Prototyp-Deklaration braucht, dann kann diese textgleich mit dem Kopf der Definition sein.
-- kw
Und ihr meint wirklich, man soll Anfängern von vornherein beibringen, Rückgabewerte zu ignorieren? Oder hat sich der Rückgabewert von 'puts()' in letzter Zeit geändert? --RalfEbert
- Ich sehe da keinen grossen Unterschied zu printf(). Jedenfalls sollten wir uns einigen, ob wir mit dieser Sammlung Lehrbeispiele für Anfänger sammeln wollen, oder ob wir hier Unterschiede demonstrieren wollen, oder ob wir eine Parodie auf industrial strength Versionen von "Hello World" schreiben wollen oder was eigentlich das Ziel der Sammlung sein sollte. -- kw
- Noch ein Nachtrag zum Prüfen des Rückgabewertes von puts() in einer Situation wie sie in "Hello, World" gegeben ist: Eine nach meinem subjektiven Empfinden gute Regel zum Umgang mit Fehlercodes ist, nur solche Fehlercodes zu prüfen, die man auch zu behandeln bereit ist. Wie sollte sich ein "Hello, World"-Programm verhalten, wenn es feststellt, dass es beim Schreiben auf stdout zu einem Fehler gekommen ist? Das gleiche Problem stellt sich (jetzt aber endgültig außerhalb des Themas dieser Diskussion) beim Prüfen des Rückgabewerts von fclose(). Nur wer bereit ist, etwas sinnvolles zu tun, wenn fclose() fehlschlägt, hat einen guten Grund, sich für den Rückgabewert von fclose() zu interessieren. -- kw
- Aus Sicht der Fehlerdelegation (Fehlerzustände so lange in der Aufrufhierarchie weiter nach oben zu reichen, bis sie eine Ebene erreicht haben, auf der sie sinnvoll behandelt werden können) sieht das etwas anders aus. Die zentrale Aufgabe des "Moduls" HelloWorld ist es, "Hello World" nach stdout auszugeben. Wenn das Modul seine zentrale Aufgabe nicht erfüllen kann, soll es dann wirklich ein Erfolgssignal zurück geben? Was würde passieren, wenn das "Modul" realloc bei Nichterfüllbarkeit seiner zentralen Aufgabe keine NULL, sondern z.B. den ursprünglich gegebenen Pointer zurück geben würde, wobei der Speicher, auf den er zeigt, aber gar nicht vergrößert wurde?
- Die Frage bleibt natürlich, ob komplette Softwaredesignprinzipien in einem Anfänger-Hello World was zu suchen haben. Auf der anderen Seite sind zum vollständigen Verständnis des Hello World-Programms bereits so viele grundlegende Konzepte vorausgesetzt (u.a. Funktionsweise der Includes, Prototypen, Definitionen-Deklarationen, das Funktionskonzept an sich, Preprozessor, Compiler, Linker, C89/99, ...), dass es darauf wahrscheinlich auch nicht mehr ankommt.
- Und zum fclose(): Etwas sinnvolles, das immer getan werden kann, ist eine Benachrichtigung des Benutzers über stderr, dass eine vom Benutzer gewünschte Aktion schief gelaufen ist - wenn auch die stderr-Ausgabe fehlschlägt, muss das allerdings nicht mehr behandelt werden, dann ist Hopfen und Malz verloren. Abgesehen davon würde ich fclose()-Fehler immer kumuliert mit fread()/fwrite()-Fehlern behandeln, selbst wenn beim Schließen einer erfolgreich und vollständig eingelesenen Datei ein Fehler auftritt, da der Fehler das "erfolgreich" und "vollständig" in Frage stellt. -- hh
- Das ist durchaus richtig. Trotzdem sollten Anfänger auf die Existenz von Rückgabeparametern bei Funktionen wie printf(), puts(), ... aufmerksam gemacht werden. So, wie ich das bisher beobachtet habe, ist der Weg beim Lernen von C etwa wie folgt:
- Nach dem Studium von hello.c
- -> "Ah ja, mit printf kann ich Texte ausgeben"
- Später
- -> "Ah prima. printf kann ja auch Daten ausgeben. Toll"
- noch später (wenn überhaupt)
- -> "Oh, printf gibt ja einen Wert zurück. Naja, den habe ich bisher ja noch nicht gebraucht, wird also nicht so wichtig sein."
- Auf diesem Weg schleicht sich dann aus reiner Gewohnheit ein relativ schlechter Programmierstil ein. Bei Verwendung von puts() in hello.c ist der Weg gleich, nur daß der zweite Schritt ausfällt. --rae
- Tatsächlich ist es aber auch so, dass printf einen Wert zurückgibt, den man "normalerweise" nicht braucht. Ich habe ihn jedenfalls seitdem ich in C programmiere (ca. 15 Jahre) nicht ein einziges Mal gebraucht. Ich glaube daher nicht, dass man einen Einsteiger in seinen ersten C-Stunden mit so etwas belasten sollte. -- HelmutLeitner
- printf() ist hier nur exemplarisch zu sehen und steht im Prinzip für eine beliebige C-Funktion. Ich verwende den Rückgabewert auch nur dann, wenn ich vermute, daß die Ausgabe blockiert werden könnte, wie es z.B. bei Filtern in Pipes geschehen kann. Es kann sein, daß ich in diesem Punkt ein bißchen extrem bin, aber ich hatte in der Vergangenheit zu oft gesagt bekommen 'Dein Treiber ist falsch, damit stürzt mein System laufend ab'. Wenn ich das Problem dann lokalisiert habe, waren es meistens Fehler bei den Kunden --- nicht geprüfte Filehandles oder Speicherallokationen, ungültige Zeiger, weil der Speicher auf dem Stack allokiert wurde und die entsprechende Funktion längst beendet war, vergessene Interrupts, ... . Ich denke, man sollte Anfängern anfangs auch etwas Selbstdisziplin beibringen. Das ist am Anfang zwar etwas mühselig, zahlt sich aber früher oder später aus. --rae
Es ist euch allen aber schon klar, daß ChristianDühl überhaupt kein HelloWorld Programm vorgeschlagen hat, sondern ein HalloWelt? Programm? -- BodoThiesen der sich diese Anmerkung nicht verkneifen konnte
Wenn hier schon über C89 und C99 und damit über Portabilität diskutiert wird, wie wäre es dann mit dieser Version?
| #include <stdio.h>
#include <stdlib.h>
int main(void)
{
int ret;
ret = printf("Hello World\n");
return ret > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
} |
|
|
Posting verschoben nach GerhardEichberger
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
Text dieser Seite ändern (zuletzt geändert: 3. Oktober 2003 8:45 (diff))