void greet()
{
printf( “Hello ” );
printf( “World!\n” );
}

int main()
{
greet();
return 0;
}

Was sehen wir hier? Nun, zuerst einmal eine neue Funktion greet mit dem Rückgabetyp void; dieser besagt, dass unsere Funktion einfach keinen Wert zurückgibt – muss sie auch nicht, sie soll uns nur grüßen. Der Funktionsrumpf besteht aus 2 Aufrufen der printf-Funktion, einfach mit aufgeteiltem String. Zusätzlich wurde in der main-Funktion der Aufruf von printf durch den Aufruf der neuen greet-Funktion ersetzt.

So weit, so einfach (hoffe ich). Aufmerksamen Lesern dürften nun aber ein paar Sachen aufgefallen sein, die ich bisher verschwiegen habe.

Als erstes wäre da die Frage, woher die Funktion printf überhuapt herkommt, und was diese ominöse erste Zeile bewirkt:

#include <cstdio>

Beides hängt zusammen; printf ist eine Funktion der sogenannten Standardbibliothek von C++. Die Standardbibliothek einer Programmiersprache ist eine Sammlung von unter anderem verschiedenen Funktionen für alle möglichen allgemeinen Zwecke; printf entstammt nun genau aus dieser. Deklariert1 ist diese Funktion in der Datei cstdio (zusammen mit vielen anderen Funktionen), welche mit der Zeile #include <cstdio> in die aktuelle Datei eingebunden wird (der Compiler weiß selber, wo er diese Datei suchen muss, deswegen klappt das). Damit wird dem Compiler die Funktion printf also bekannt gemacht und wir können sie problemlos aufrufen.

1 Über den Unterschied zwischen Deklaration und Definition schreibe ich ein andermal.

Eine weitere auftauchende Frage könnte sein, was das \n am Ende des Hello World!-Stringes zu bedeuten hat. \n ist eine sogenannte Escape-Sequenz; das sind kurze Zeichenketten, die besondere Symbole darstellen. \n ist dabei das Zeichen für den Zeilenumbruch, da wir am Ende unseres Strings einen solchen haben wollen, ihn aber nicht einfach in den String schreiben können (das verbietet C++). Der folgende Code ist also ungültig:

void greet() { printf( "Hello World! " ); }

Escape-Sequenzen beginnen immer mit einem Backslash, gefolgt von (meist) einem oder mehreren Buchstaben, Symbolen oder Ziffern, welche die genaue Escape-Sequenz beschreiben. Das bringt natürlich sofort die Frage mit sich, wie man denn dann einen normalen Backslash innerhalb eines Strings darstellt – das geschieht wiederum über eine Escape-Sequenz, und zwar per Doppelbackslash: \\

Eine letzte wichtige Frage ist noch offen: woher weiß das Programm, dass es gerade bei der main-Funktion anfangen soll, und warum liefert die eigentlich eine ganze Zahl zurück? Beides ist Konvention; in der Standardeinstellung verlangen alle Compiler in einem zu kompilierenden Programm eine Funktion mit dem Namen main, welche als sogenannter Einsprungpunkt der Anwendung genutzt wird, sprich, welche automatisch bei Programmstart als erstes aufgerufen wird. Dass die main-Funktion eine ganze Zahl als Rückgabewert erwartet, ist ebenfalls Konvention. Früher wurde damit häufig angegeben, ob eine Anwendung erfolgreich beendet wurde oder ob ein Fehler auftrat – die zurückgegebene Zahl gab einen Hinweis auf den Fehler. Moderne Compiler akzeptieren aber auch eine main-Funktion ohne Rückgabewert, sprich mit der Signatur void main() und dann ohne die Zeile return 0;.

Das war der erste Artikel zum Thema Funktionen. Fragen wie immer in die Kommentare oder (bei speziellen Fragen) gern auch per Mail. Im nächsten Artikel wird es dann (vermutlich) um das zweite wichtige Konzept von Programmiersprachen gehen, nämlich die Variablen. Seid gespannt!

1 / 2

Kommentare (28)

  1. #1 Dr. Webbaer
    April 5, 2013

    Welchen Ident-Style bevorzugen Sie warum?

    MFG
    Dr. W (der den Whitesmiths Style bevorzugt, dem auch das Isolieren der Zeichenketten per Whitespace auffiel)

  2. #2 Marcus Frenkel
    April 5, 2013

    Ich habe meinen eigenen Einrückungsstil. Ob der zufällig mit einem formal festgehaltenen identisch ist, weiß ich nicht. Ich maximiere meine Einrückung auf maximale Lesbarkeit und Übersichtlichkeit, so dass sich einzelne Bestandteile einer Anweisung oder eines Ausdruckes auf einen Blick voneinander trennen lassen. Leerzeilen und -zeichen kosten kein Geld und moderne Monitore sind groß genug, um viele davon zu vertragen.

  3. #3 Gast
    April 5, 2013

    Leerzeilen werden bestimmt als nope-Instruktionen umgesetzt 😉

  4. #4 Dr. Webbaer
    April 5, 2013

    Ich maximiere meine Einrückung auf maximale Lesbarkeit und Übersichtlichkeit, so dass sich einzelne Bestandteile einer Anweisung oder eines Ausdruckes auf einen Blick voneinander trennen lassen.

    Wobei hier einerseits jeder seine eigene Meinung zu haben scheint und andererseits doch recht wüst diskutiert wird, welcher Indent Style nun besonders günstig ist. – Sehr richtig natürlich angemerkt, dass nichts gegen den Nutzen von Whitespace spricht; der Schreiber dieser Zeilen fand es bspw. recht schön, dass die Zeichenketten im Artikel sanft oder samtig behandelt worden sind. Das macht den Code besser. Es spricht auch nichts gegen Programmiersprachen, die Whitespace, also auch den Zeilenumbruch des Editors (vs. ‘\n’), uninterpretiert lassen, also dem Entwickler mehr Raum geben.

    MFG
    Dr. W

  5. #5 Stefan W.
    https://demystifikation.wordpress.com
    April 5, 2013

    Aufgabe 1: Finden Sie alle Vorkommen des Begriffs “sogenannt” und markieren sie diese gelb.

    Aufgabe 2: Wieso ist mal von echten Strings, mal von nur sogenannten die Rede? Woran macht der Autor den Unterschied fest?

    Der Funktionskopf wird übrigens nicht Definition, sondern Deklaration genannt – die Definition ist der Funktionsbauch. Wenn die Funktion nichts zurückliefert spricht man auch von einer Prozedur. Main ist aber nicht auch oft einfach nur eine Prozedur, sondern der Compiler fügt implizit ein ‘return 0;’ ein, wenn keins existiert.

    Und nach meiner Erfahrung springt man nach Aufruf einer Prozedur oder Funktion nicht an die Stelle vor, sondern hinter dem Aufruf – sonst würde man ja die Funktion wieder aufrufen müssen, und in eine Endlosrekursion verfallen.

  6. #6 rolak
    April 5, 2013

    Wieso .. sogenannten

    Weil die so bezeichneten Dinge so genannt werden, Stefan W., infolgedessen besteht auch kein Unterschied, da es sich um ein und dasselbe Ding handelt, nur um zwei Bezeichnungen.

    sondern hinter dem Aufruf

    Einerseits dürfte das ‘vor’ im post ein zeitliches sein (‘an die Stelle, an der man vor dem Aufruf war’), andererseits erfolgt der Rücksprung tatsächlich genau an die Stelle, an der der Aufruf ausgeführt wurde – die bedingt durch den Opcode/Operand-Konsum zur Interpretation des Maschinencodes zugegebenermaßen mehr oder weniger weit ‘hinter’ dem Code für den Aufruf liegt. Gibt ja auch Parameterübergabe via deren Platzierung hinter dem Code für den Aufruf, insbesondere in stacklosen Momenten; könnte allerdings auch insgesamt als aufgeblasener.Aufruf angesehen werden.

  7. #7 Sepp
    April 5, 2013

    @rolak: “sogenannt” bedeutet nicht, dass etwas so genannt wird, sondern impliziert, dass etwas fälschlicherweise als solches bezeichnet wird. Ein sogenannter Experte ist zum Beispiel kein Experte, sondern jemand der sich für einen solchen ausgibt oder dafür gehalten wird.

    @Stefan W.: Definition ist in diesem Kontext der richtige Begriff und wird im Allgemeinen auch so verwendet. Bei C++ wird typischerweise der Teil im Header Deklaration genant, die Implementierung, inklusive des Kopfs, Definition.

    Aber mal zum Artikel: Warum verwendest du das veraltete cstdio und nicht den C++-Weg über IO-Stream? Ansonsten finde ich den Teil aber gut.

  8. #8 Marcus Frenkel
    April 5, 2013

    @Sepp

    “sogenannt” bedeutet nicht, dass etwas so genannt wird, sondern impliziert, dass etwas fälschlicherweise als solches bezeichnet wird. Ein sogenannter Experte ist zum Beispiel kein Experte, sondern jemand der sich für einen solchen ausgibt oder dafür gehalten wird.

    Es hängt immer vom Kontext ab. Beim Experten – ja. Weil da Ironie mitschwingt. Aber “sogenannt” ist ein ganz normales, wertfreies Wort, welches ich auch weiterhin als solches benutzen werde. Nicht immer alles so negativ sehen. 😉

    Was cstdio und iostream angeht: Objektorientierung kommt noch. Wir sind noch ganz am Anfang.

  9. #9 rolak
    April 6, 2013

    impliziert, dass etwas fälschlicherweise als solches bezeichnet wird

    Nur um den Reigen zu komplettieren, Sepp: Diese von Dir beschriebene Verwendung ist durchaus möglich, allerdings nicht die im eigentlichen, sondern die im übertragenen Sinne, also nix mit ‘implizieren’.
    Das mit Def/Dek beschreibt Stefan W. durchaus richtig, da sich Markus’ ‘Definition’ explizit auf die Kopfzeile bezieht und selbst im von Dir herangezogenen Kontext, also der kompletten Funktions-Beschreibung in der Quelle, deutlich zu trennen ist, dh die vollständige Variante ist eine Kombination Def+Dek. Doch das will Markus ja ein andermal komplett beschreiben…

  10. #10 Dr. Webbaer
    April 6, 2013

    ‘Sogenannt’ war schon ganz richtig verwendet, sofern das der Autor dieser Nachricht im Augenblick überschauen kann, und dient berechtigterweise der Betonung einer Namensgebung oder Terminologie.

    Metaphorisch kann sozusagen im Subtext oder sozusagen negativ konnotiert eine Abwertung einhergehen.
    Was in der rein fachlichen Kommunikation aber eher nicht zu erwarten wäre.

    Die Unterscheidung von Prozeduren (ohne Rückgabewert oder Status) und Funktionen ist nicht sehr günstig, btw.

    MFG
    Dr. W (der den Funktionskörper auch nicht als ‘Definition’ bezeichnen würde)

  11. #11 Dr. Webbaer
    April 6, 2013

    Nachtrag:
    Es gäbe auch noch einiges anzumerken, was Funktionen eigentlich sind, sozusagen das Wesen der Funktion betreffend. Hier könnte man ausbauen, bspw. das Tautologisch/Mathematische manche Funktionen betreffend oder den Nachbau bzw. die Unterstützung von Objekten, von Geschäftsobjekten oder die (grundsätzlich zu meidenden) Mischformen.

    Auch die Logikverteilung, spätere Wartbarkeit, Weiterentwicklungs- und Wandlungsfähigkeit betreffend.

    Kommt aber sischerlich noch…

  12. #12 DeLuRo (DerLustigeRobot)
    April 6, 2013

    Thema:   das Wort “sogenannt”

    Es ist doch traurig, wie wenig heute manche Worte der deutschen Sprache noch im eigentlichen Sinne verstanden, und damit “ältere” Schreiber oder eben ältere Texte richtig verstanden werden. Leider muss heute offenbar alles irgendwie ironisiert und ironisch verstanden, alles “hintenrum” ausgedrückt werden. — Das geht in wissenschaftlicher Sprechweise nicht!

    “Sogenannt” ist eine Betonung des nachfolgenden Begriffs als ein referenzierbares Schlüsselwort, das man genau so hinschreiben und nicht durch eine Umschreibung ersetzen wollte (und inhaltlich nebensächlich, da man ja sieht, dass da ein Wort steht). — Sprache unterliegt nun mal (manchmal “leider”, manchmal notwendigerweise) einer sogenannten sprachlichen => “Drift (Linguistik)”, und zwar im Sinne des sogenannten “Sprachgebrauchs”. Das könnte man jetzt also nachschlagen. 😉

    Was wir aber am wenigsten brauchen, ist ein nebulöser quasi-wissenschaftlicher Vortrag über verquaste abstrakte Themenstreifschüsse, die viel zu weit davon abliegen. — Thema verfehlt. Dafür gibt es nun das Prädikat “Netz-Ramme”!.

  13. #13 Dr. Webbaer
    April 6, 2013

    @DerLustigeRobot

    Beim guten alten ‘Sogenannt’ wie auch beim ‘Sozusagen’ gibt es einen gewissen Nachholbedarf, was das jüngere Personal betrifft – ist es doch geschult worden politischen Kräften zu folgen, die das anscheinend angestrebte Dummdeutsch nutzen, um allgemein verstanden zu bleiben, dabei das explizit angekündigtes Wechseln der Abstraktionsebenen [1] streng meidend.

    Fürwahr.

    MFG
    Dr. W

    [1] aus welchen Gründen auch immer, lol

  14. #14 DeLuRo (DerLustigeRobot)
    April 6, 2013

    Zum Thema Definition & Deklaration, Funktion & Prozedur etc. meine bescheidene Meinung.

    Ich möchte da Marcus nicht allzuweit vorgreifen, aber vielleicht sollte man unterscheiden zwischen der Form in der eine Funktion hingeschrieben wird (Syntax) und der Bedeutungsebene (ein Semantiklevel), die an den Compiler oder Interpreter gerichtet ist.

    Man schreibt formal den Funktionskopf und einschließlich der äußeren geschweiften Klammern den Funktionsrumpf (Body, nicht “Bauch”, Stefan W.; eine Funktion hat kein “Bauchgefühl” — aber gut, war wohl Ironie). Der Nutzen kommt dann in der nächsten Stufe: beim Rumpf ist es ganz klar und einfach die Definition dessen, was die Funktion tut. Der Kopf dagegen zeigt –wie Marcus auch schreibt– die Signatur der Funktion: Name, Stelligkeit (Anzahl Argumente), die Typen der einzelnen Argumente und des Rückgabewerts. Die Signatur kann man abstrakter betrachten, sie ist eine Vereinbarung zwischen verschiedenen Programmteilen oder auch zwischen Programmierer und Compiler, wie eine Funktion im Aufruf zu gebrauchen ist. Eine Form der Signatur wird auch verwendet, wenn Funktionen in einer Bibliothek (Library, DLL) an andere “Nutzer” angeboten wird.

    Wenn ich also nur den Funktionskopf hinschreibe, gebe ich die Signatur als Deklaration bekannt, mithin als Gebrauchsvorschrift oder -vereinbarung. Wenn ich den Rumpf mit hinschreibe ist es offenbar eine Definition, was aber, da Kopf bzw. Signatur auch dort stehen, die Deklaration mit be-inhaltet. Definition und ggfs. weitere Deklarationen müssen konsistent sein, oder Compiler bzw. Linker wissen möglicherweise nicht, was sie tun sollen.

     
    Das Wesentliche einer Prozedur ist nicht, dass sie keinen oder einen Standard-Nullwert zurückgibt, sondern dass ihre Wirkung in den Seiteneffekten liegt, also den Wirkungen, die nicht im Rückgabewert stehen. Das erklärt Marcus sicherlich noch — im Beispiel ist der Seiteneffekt die Ausgabe per printf(). Eine Funktion im streng mathematischen Sinne oder in einer streng funktionalen Sprache hat nie einen Seiteneffekt — daher der Begriff “Prozedur” zur Unterscheidung. In Cxx kann jede Funktion auch prozedural sein.

    Der Standard-Rückgabewert 0 von main() ist begründet durch Unix-Derivate (als Ziel vieler C-Programme), die 0 als ein “Okay” erwarten, wogegen jeder andere Wert als Fehlercode interpretiert wird.

  15. #15 Dr. Webbaer
    April 6, 2013

    Das Wesentliche einer Prozedur ist nicht, dass sie keinen oder einen Standard-Nullwert zurückgibt, sondern dass ihre Wirkung in den Seiteneffekten liegt, also den Wirkungen, die nicht im Rückgabewert stehen.

    Das ist nicht gant richtig, wie Sie bspw. am hier vorgestellten “Hello World”-Programm bzw. an der Prozedur greet() ablesen können. Die Unterscheidung zwischen Prozeduren und Funktionen ist eher akademisch, man meint mit Prozeduren idR den Aufruf einer Logik im Rahmen eines Scopes, der nicht explizit benannt wird.

    HTH
    Dr. W

  16. #16 ulfi
    April 7, 2013

    @Dr.W
    nein, DeLuRo hat vollkommen recht. Die Wirkung von greet() ist ein reiner Seiteneffekt, also ist es eine Prozedur. Und dann wurde noch korrekt zwischen Funktionen im funktionalen paradigma und Funktionen mit Seiteneffekten unterschieden.

    Wie so häufig haben sie nur begrenzt Ahnung 🙁

  17. #17 rolak
    April 7, 2013

    Wenn ich den Rumpf mit hinschreibe ist es offenbar eine Definition

    Weiterhin, DeLuRo, weiterhin plädiere ich in dem Falle auf ‘Deklaration und Definition’, doch vielleicht ja nur, weil mir bisher noch nichts Überzeugendes dagegen untergekommen ist…

    begründet durch Unix-Derivate (als Ziel vieler C-Programme), die 0 als ein “Okay” erwarten

    ..was, da üblicherweise nur der Fehlerfall gesondert behandelt wird und da Unix ja auch ein Produkt von C-Programmen ist, wiederum durch das Konstrukt ‘if <int> CmdBlock;’ begründet ist und damit letztlich durch den cpu-einfachen, öfters (je nach Kontext) sogar kostenfrei gelieferten Tests auf 0.

  18. #18 Dr. Webbaer
    April 7, 2013

    Ulfi, der Schreiber dieser Zeilen sieht greet() als Funktion, hat sie aber im Kontext, recht passend, wie auch Sie finden, eine Prozedur genannt.

    MFG
    Dr. W (den auch die Meinung der hiesigen Spezialkraft zur Abgrenzung Funktion/Prozedur interessieren würde)

  19. #19 Marcus Frenkel
    April 7, 2013

    Ich treffe die Unterscheidung zwischen Prozedur und Funktion/Methode in der Regel nicht, bzw. nutze den Begriff Prozedur gar nicht. Der Mehrwert der Verwendung des Begriffes “Prozedur” ist in meinen Augen zu gering, als dass es gesonderter Beachtung wert wäre (nicht natürlich in Sprachen, die Prozeduren und Funktionen syntaktisch unterscheiden – C macht das ja nicht). Hinzu kommt die, wie in dieser Diskussion sich abzeichnende, unscharfe Definition des Begriffes. Jeder versteht in kleinen Details etwas anderes darunter – warum dann mit dem Begriff aufhalten, wenn er nichts bringt?

    Oder anders, gefragt: wo liegt der Vorteil darin, begrifflich zwischen Prozeduren und Funktionen zu unterscheiden, insbesondere in Sprachen der C-Familie?

  20. #20 rolak
    April 7, 2013

    wo liegt der Vorteil darin

    Wohl eher in der Psychologie, denn praktisch gibt es da imho nichts, Marcus. Entweder sind Funktionen Prozeduren mit einem impliziten ~by-ref-Parameter, dem Ergebniswert, oder Prozeduren sind Funktionen mit einem nicht definierten und damit auch nicht auswertbaren Ergebniswert.

    Verschwenderischer Umgang mit reservierten Wörtern.

  21. #21 Dr. Webbaer
    April 7, 2013

    Der Mehrwert der Verwendung des Begriffes “Prozedur” ist in meinen Augen zu gering, als dass es gesonderter Beachtung wert wäre (…)

    Klingt schlau, danke,
    MFG
    Dr. W

  22. #22 m
    April 7, 2013

    Wenn hier schon alle so schön am Korinthenkacken sind: Bei standardisierten Sprachen gibt es ja Die WahrheitTM:

    ISO/IEC 14882 §3.1 2: A declaration is a definition unless it declares a function without specifying the function’s body …und zig andere Fälle

    Eine Definition ist also auch eine Deklaration.

    Und bei dem impliziten Rückgabeparameter von Funktions-Prozeduren sollten wir out sagen statt by-ref; schließlich werden zumindest kleine Typen üblicherweise by-copy in einem Register zurückgegeben.

    Und jetzt haben wir die Programmierneulinge hier wohl genug verwirrt.

  23. #23 rolak
    April 7, 2013

    sollten wir out sagen

    Nu ja, m, das ‘~’ hatte keinen heimatlichen Bezug, stand für ‘ungefähr so wie’, gerade weil dieser Parameter so variabel gehandhabt wird und nur ein Regelwerk besteht, wohin der Wert für die Übergabe gesteckt werden muß (ref4copy?).
    Eigentlich brauchts ja gar keinen neuen Typus, da der mit ‘Funktionsergebnis’ schon bekannt ist.

  24. #24 Nicolai
    April 9, 2013

    Evtl. noch erwähnenswert: Der C++-Standard erlaubt das Verwenden von void als Rückgabetyp des Einstiegspunkt nicht. Auch wenn die Compiler das so akzeptieren – korrektes C++ ist es nicht. Unter C ist es (zumindest in älteren Standards, weiß das spontan nur von ANSI-C) legitim.

    Die Quellen dazu kann ich gerne nachreichen, bin im Moment aber leider unterwegs.

  25. #25 m
    April 10, 2013

    Ups, rolak, die Tilde hab ich irgendwie übersehn; da hab ich wohl beim pingelig sein komplett versagt. hm.

    Nicolai, gerade der Typ vom main macht C meines Erachtens für die Lehre problematisch, gerade in Zeiten in der die Kommandozeile nicht mehr im Mode ist. Ein Freund von mir, Mechaniker, hat in seinem Programmierkurs am Anfang gelernt: es heißt int main(int, char**) weil das so ist! Was is denn das für ein Einstieg? Ich finde es gut, das Markus das erst einmal ignoriert.

  26. #26 rolak
    April 11, 2013

    komplett versagt

    Lächerlich, m – solche Winzigkeiten sind schnell übersehen, das ist völlig normal. Beim Kommentar-Lesen wird ja iA nicht die gleiche Informationsdichte erwartet wie bei der Code-Inspektion…

    weil das so ist

    Dieser Satz sollte bei jedem, der ihn hört, aber so was von die Alarmglocken klingeln lassen…

  27. #27 Dr. Webbaer
    April 17, 2013

    Im Artikel wird die Funktion als Routine, was sie für das Gerät auch ist.
    Aber Funktionen bilden auch bestimmte Handlungsabläufe oder Sichten nach, alles das Geschäft oder die Erkenntnis betreffend.
    Routinen werden auch dementsprechend geordnet und verwaltet.

    HTH
    Dr. W

  28. #28 Stefan W.
    https://demystifikation.wordpress.com
    April 24, 2013

    Dass Prozeduren Seiteneffekte haben weil sie keine Rückgabe liefern ist ja genau der Punkt. Welchen Sinn sonst hätte es eine aufzurufen?

    Man kann natürlich lange hincoden, ohne dem große Aufmerksamkeit zu schenken, weil es ja auch so trivial ist.

    Trivial ist auch die Verwendung von sogenannt. In einem einstündigen Vortrag ist eine einzelne derartige Äußerung verschmerzbar. Wenn man aber andere in ein Thema einführt, wo in jedem 2. Satz ein ‘sogenannt’ vorkommt, ist es einfach nur häßlich. Es fügt ja im Regelfall kein Wissen hinzu. Der Schüler weiß, dass er noch nicht weiß was eine Deklaration ist, das muss ihm der Dozent nicht sagen. Dass der Dozent weiß, was der Schüler noch nicht weiß ist ebenfalls trivial – dafür gibt er ja den Unterricht.

    Wenn also vernünftige, inhaltliche Gründe ausfallen um das Wort zu benutzen, dann bleibt noch die Ironie, d.h. der Leser muss ständig Ausschau halten, ob er nicht eine Anspielung übersehen hat, oder es ist ein reines Füllwort, ein Lückenfüller, um den Text länger zu machen.

    square (4) ist definiert als 4 * 4. Das ist doch ganz einfach.

    int sqrt (int a); ist nur die Deklaration. Ob Body nun Bauch oder Rumpf, von mir aus auch Körper heißt – geschenkt.