Hallo Besucher, der Thread wurde 13k mal aufgerufen und enthält 93 Antworten

letzter Beitrag von Zirias/Excess am

C Frage ist folgendes zulässig?

  • So wird das in dem recht bekannten Lehrbuch "C als erste Programmiersprache" benutzt


    Sie ist ja auch nicht falsch! Sie gefällt mir persönlich jedoch am wenigsten - eben aus besagtem Grund.
    Ich habe beruflich auch bisher keine Coding Guidelines beachten müssen, die diesen Stil vorgegeben haben.


    Daher *vermute* ich, dass die Masse an professionellen Programmierern sich für Variante 1 oder 3 entschieden hat.

  • Ich *vermute*, dass die Masse an professionellen Programmierern sich für Variante 1 oder 3 entschieden hat.

    Argumentieren kann man für alle drei Varianten, aber vermutlich hast du da recht: Variante 1 entspricht am ehesten der C Denkweise, Variante 3 ist Stroustrup-Stil.


    Interessant wäre mal, ob solche Bücher auch erklären, wieso ein bestimmter Stil verwendet wird ...

  • Ich habe beruflich auch bisher keine Coding Guidelines beachten müssen, die diesen Stil vorgegeben haben.


    In den umstrittenen MISRA-C-2004-Guidelines tauchten solche Beispiele auf:


  • Ups... die habe ich schon ganz vergessen verdrängt! :schande:



    Sind die doch mein fast tägliches Brot... aber ich trickse mich da durch: API - also das was der Kunde sieht
    ist MISRA-konform. Der Rest nicht immer.


    Hab' grad mal in mein aktuelles Projekt geschaut:
    In den Headern vom Autosar OS (!) sind auch jede Menge Kommentare über absichtliche Verletzungen der MIRA Spec. drin. :lol27:

  • Interessant wäre mal, ob solche Bücher auch erklären, wieso ein bestimmter Stil verwendet wird ...


    Begründet wird das leider nicht. Aber auch Arrays werden komisch deklariert:

    Zitat

    Die allgemeine Form der Definition eines eindimensionalen Arrays ist:

    Code
    1. Typname Arrayname [GROESSE]; /* GROESSE ist die Anzahl der Array-Elemente */


    Konkrete Beispiele hierfür sind:

    Code
    1. int alpha [5]; /* Array aus 5 Elementen vom Typ int */
    2. char beta [6]; /* Array aus 6 Elementen vom Typ char */
  • Begründet wird das leider nicht. Aber auch Arrays werden komisch deklariert:

    Das sieht mir nicht nach einem durchdachten Stil aus, sondern einfach nach "zusätzliche Leerzeichen machen alles lesbarer". Gefällt mir zwar nicht, aber das andere Ende des Spektrums, nämlich fast gar keine Leerzeichen, fände ich noch schlimmer.


    Danke auch für den MISRA-Link, sehr informativ - so einen Quatsch benutzen zu müssen, ist mir zum Glück bisher erspart geblieben. Ich mach zwar viel embedded-Kram, aber nicht in der Autoindustrie.

  • Danke auch für den MISRA-Link, sehr informativ - so einen Quatsch benutzen zu müssen, ist mir zum Glück bisher erspart geblieben. Ich mach zwar viel embedded-Kram, aber nicht in der Autoindustrie.

    Ja... die "Blechbieger" (SCNR!) haben schon komische Ideen zum Teil; aber wer die Band bezahlt, bestimmt was gespielt wird.
    Ich hab' schon lange aufgehört das alle zu hinterfragen. :P


    Und am besten ist es ja, wenn sie sich selbst nicht dran halten!! :D Was Jupiter darf...



    Zitat von Mac Bacon

    "zusätzliche Leerzeichen machen alles lesbarer"

    Codier-Plenken

  • Danke auch für den MISRA-Link, sehr informativ - so einen Quatsch benutzen zu müssen, ist mir zum Glück bisher erspart geblieben. Ich mach zwar viel embedded-Kram, aber nicht in der Autoindustrie.

    Ich musste zum Glück nie was damit konformes schreiben, aber IIRC lesen sich manche der Regeln definitiv wie "wir hatten da mal einen Compiler bei dem das Feature kaputt war und haben es daher in der Spec verboten"

  • Interessant wäre mal, ob solche Bücher auch erklären, wieso ein bestimmter Stil verwendet wird ...


    Die bisher lakonischste Begründung habe ich "C++ Primer" von Lippman und Lajoie gefunden. In der deutschen Ausgabe steht:


    Zitat

    Aus Gründen der Übersichlichkeit ist die Schreibweise

    Code
    1. string *ps;

    der Schreibweise

    Code
    1. string* ps;

    vorzuziehen.

  • @Deep4 das ist in C++ ja auch völlig ok. Stroustrups Begründung ist in sich völlig logisch und entspricht auch dem, wie es in moderneren Sprachen (java, c#, ...) gehandhabt wird.


    Die "Schwäche" dabei ist, dass Stroustrup seine neue Sprache quellcodekompatibel zu C haben wollte (was heute durch unterschiedliche Weiterentwicklungen sowieso nicht mehr der Fall ist) und deshalb nichts an der bestehenden Grammatik geändert hat. Das führt dazu, dass eine Deklaration string* a, b; nicht das tut, was man nach Stroustrups Logik erwarten würde. Der simple Workaround ist, auf Mehrfachdeklarationen zu verzichten, was man heute sowieso tut, weil es der Lesbarkeit generell guttut. Leider funktioniert die "logische" Deklaration eines Arrays char[] a; (in moderneren Sprachen auch üblich) in C++ trotzdem nicht, das beißt sich mit der C Grammatik.


    Ich persönlich finde es falsch, diesen Stil in C anzuwenden. In C++ dagegen ist es sinnvoll, weil sich die meisten C++-Programmierer selbstverständlich an Stroustrups Empfehlungen orientieren.

  • Ich arbeite mit einer sehr großen Codebase, und um den Code lesbarer zu halten verwenden wir automatisch generierte Typdefinitionen für gängige Konstrukte. Also z.B.:


    pcFoo für const Foo*
    rcFoo für const Foo&
    ppFoo für Foo**
    usw.


    Damit ist dann eh jedem klar das der Pointer eine Art von Typ ist, und nicht eine Art von Variable.

  • In C++ dagegen ist es sinnvoll, weil sich die meisten C++-Programmierer selbstverständlich an Stroustrups Empfehlungen orientieren.


    Das witzige ist, dass in Stroustrups Buch auch

    Zitat

    6.3.2 Declaring Multiple Names
    ...

    Code
    1. int∗ p, y; // int* p; int y; NOT int* y;
    2. int x, ∗q; // int x; int* q;
    3. int v[10], ∗pv; // int v[10];int* pv;

    zu finden ist.

  • @ogd Da hört es für meine Begriffe aber auf mit der Logik. Naja, dem Herrn Stroustrup hätte man sagen können you can't have your cake and eat it -- also entweder neue (und durchaus sinnvolle!) Logik, oder Kompatibilität zu C. Naja, jetzt ist es wie es ist ;)


    @mrsid also DAS ist für meine Begriffe eher bewusste Verschleierung. In den meisten Fällen muss der Programmierer es wissen, wenn er es mit einem Pointer zu tun hat. Klar, wenn man jetzt weiß, dass "p" für pointer steht, geht das, aber warum? Die Sprache hat doch schon ein Symbol für Pointer, das jeder kennt: *


    Microsoft hatte ja genau diesen Ansatz im win32 API, finde ich auch dort schauderhaft --- zum Glück gibt es in .NET nichts vergleichbares mehr.

  • Dafür hat uns C# das in Java abgeschaffene GOTO wieder geschenkt :)

    Ganz ehrlich, ich suche bis heute nach einem tatsächlichen praktischen Anwendungsfall dafür. In C ist die Sinnhaftigkeit von GOTO weitgehend unumstritten, da sorgt es richtig angewendet für eine übersichtliche Codestruktur, wenn in einem Ablauf an verschiedenen Stellen Fehler behandelt werden müssen. Das braucht in C# aber keiner -- ob man nun mit Exceptions arbeitet oder funktional mit einem Error-Typ, die Garbage Collection (und notfalls das IDisposable-Pattern) sorgen schon dafür, dass immer korrekt aufgeräumt wird, jedenfalls wenn der Programmierer keinen Mist baut. Selbst in C++ kann man ähnlich argumentieren, zumindest wenn da konsequent das RAII Prinzip umgesetzt wird.


    Vorstellen könnte ich mir, dass es auch in C# mal von Vorteil sein könnte, aus einer verschachtelten Struktur komplett auszubrechen (break bricht ja nur ein Level aus), allerdings ist mir so eine Notwendigkeit in der Praxis noch nicht begegnet.


    Aber: Ich finde es trotzdem gut so. Ich bin kein Freund davon, ein Sprachfeature aus "dogmatischen" Gründen wegzulassen. Ob sinnvoll/sauber programmiert wird hängt immer zuerst am Programmierer. Wenn jemand unbedingt "goto" in Java möchte bastelt er sich eines (da gibt's Varianten mit Missbrauch von Exceptions, oder von switch/case, ...), was dann noch viel größerer Murks wird, weil man es auf den ersten Blick nicht entziffern kann ;)

  • @mrsid also DAS ist für meine Begriffe eher bewusste Verschleierung. In den meisten Fällen muss der Programmierer es wissen, wenn er es mit einem Pointer zu tun hat. Klar, wenn man jetzt weiß, dass "p" für pointer steht, geht das, aber warum? Die Sprache hat doch schon ein Symbol für Pointer, das jeder kennt: *

    Nein, das hat schon seinen Sinn. Es geht ja hier nicht um einen einzelnen Programmierer, sondern es arbeiten hier Dutzende gemeinsam am Projekt. Vorallem bei const-correctness wollen wir keine Abstriche machen, und daher geht es hier nicht um das p sondern um das c.
    Ausserdem ist ja der * nicht unbedingt das beste Symbol für Pointer, das ist ja dummerweise auch schon der Multiplikationsoperator.


    Und weiters macht das auch Sinn bei viel verwendeten Template Typen, wie z.b. Arrays/Vektoren oder Listen. Ich kann z.b. eine Instanz unserer Array Klasse auf diese Weise deklarieren:


    Code
    1. apcKid lots_of_kids; // ein Array (vector) mit pointer auf const Kid Objekte.


    Will ich das Array übergeben, kann ich als Parameter einer Funktion einfach rcapcKid oder rapcKid verwenden (je nachdem ob const oder nicht).



    Code
    1. void CheckMyKids(rcapcKid inKids);

    Ausgschrieben wäre das in unserem Fall:

    Code
    1. void CheckMyKids(const PtrArray< const Kid* > &);

    Und das ist nur ein sehr simpler Fall. Wenn man sich das jetzt für viele Funktionen mit vielen Parametern vorstellt, dann spart das in Summe nicht nur wirklich viel Tipparbeit (vorallem beim Refactoren) sondern macht den Code deutlich lesbarer weil einfach weniger Bäume im Wald rumstehen.

  • Nein, das hat schon seinen Sinn. Es geht ja hier nicht um einen einzelnen Programmierer, sondern es arbeiten hier Dutzende gemeinsam am Projekt.

    Genau deshalb finde ich es schlimm. Es definiert eine neue Variante der Sprache, an die sich jeder Programmierer erstmal gewöhnen muss.

    Vorallem bei const-correctness wollen wir keine Abstriche machen, und daher geht es hier nicht um das p sondern um das c.

    Auch dafür gibt es ein wohlbekanntes Keyword: const. Was dagegen das "c" bedeuten soll muss ich erstmal nachschlagen. Schlimmer noch, ich weiß erstmal gar nicht, worauf sich das const bezieht -- auf den Wert, auf den Pointer, auf beides?

    Ausserdem ist ja der * nicht unbedingt das beste Symbol für Pointer, das ist ja dummerweise auch schon der Multiplikationsoperator.

    Das KANN man so sehen, trotzdem weiß jeder, der C und/oder C++ beherrscht, was das ist. Von einem "p" im Typnamen kann man das nicht behaupten.

    [Beispiel]
    macht den Code deutlich lesbarer

    Sehe ich auch bei dem Beispiel genau umgekehrt. Bei der "ausgeschriebenen" Variante ist mir direkt klar, was es bedeutet.

  • Genau deshalb finde ich es schlimm. Es definiert eine neue Variante der Sprache, an die sich jeder Programmierer erstmal gewöhnen muss.

    Das dauert erfahrungsgemäss zirka 30 Minuten. Neue Teammitglieder gewöhnen sich im Nu daran. Man kann ja auch jederzeit zur Deklaration des Typs springen und sieht dann genau was es ist.
    Die Sprache ist bei uns nicht heilig, sondern Mittel zum Zweck und wird angepasst wenn das ganz pragmatisch was bringt.


    Was dagegen das "c" bedeuten soll muss ich erstmal nachschlagen.

    Ja, aber nur einmal. Und danach hast du Jahre (oder in unserem Fall bereits Jahrzehnte) lang kürzeren, besser lesbaren und leichter wartbaren Code. Das wiederum erleichtert besseres Wachstum der Codebase bei gleichzeitiger Verringerung des "technical debt".

  • Das dauert erfahrungsgemäss zirka 30 Minuten.

    Glaube ich nicht. Da wird man als neues Teammitglied IMMER WIEDER nachschlagen, gerade weil es so viele verschiedene typedefs gibt.


    Kann natürlich jeder machen wie er will. Aber diverse Diskussionen (z.B. hier) legen nahe, dass eine Mehrheit Code ohne solche typedefs lesbarer findet (die akzeptierte Antwort dort geht auf genau den Fall ein, in dem viele so ein typedef sinnvoll finden: Genau dann, wenn man gar nicht wissen SOLL, dass es sich um einen Pointer handelt).