Top 10 NoGos in C



  • Danke für das nächste NoGo

    dachschaden schrieb:

    size_t i;
        scanf("%lu",&i);
    ....
        printf("Das haben Sie gut gemacht, die Zahl ist '%lu'\n",i);
    

    Falsche Formatspecifier.

    Wir haben mittlerweile 2014. Da darf man ruhig mal einen 15 Jahre alten Standard dem 25 Jahre Altem vorziehen.



  • DirkB schrieb:

    Falsche Formatspecifier.

    Auch hier darf ich wieder mit Fingern auf andere zeigen:

    man scanf schrieb:

    z wie für h, der nächste Zeiger ist aber ein Zeiger auf ein size_t. Dieses Änderungszeichen wurde in C99 eingeführt.

    Und das habe ich jetzt schnell in einer Minute gehackt, verzeih mir bitte, dass ich keine Prüfung auf die Architektur und damit eine Definition für einen korrekt Formatspezifizierer gemacht habe. :p

    DirkB schrieb:

    Wir haben mittlerweile 2014. Da darf man ruhig mal einen 15 Jahre alten Standard dem 25 Jahre Altem vorziehen.

    Recht hast du schon, keine Frage. Aber wenn die Compilerhersteller *hust*Microsoft*hust* da blockieren, ist es egal, wenn du deine Software mit total neuen und auch zugegebenermaßen tollen Features programmierst. Kompiliert nicht => Tonne.
    Ja gut, gibt auch andere Compiler. Aber ich seh das so, dass am immer für die kleinste gemeinsame Menge programmieren sollte. Außerdem programmiert man meines Erachtens so auch bewusster - aber das ist nur meine Meinung.


  • Mod

    Ich würde da ganz andere Prioritäten setzen. Wen interessiert ein Cast beim malloc? Klar, es zeigt, der Programmierer schreibt irgendwo ab und das vermutlich aus dubioser Quelle, aber es ist nicht krass falsch, bloß unnötig und schlechter als kein Cast.

    Ohne besondere Reihenfolge, außer den ersten und letzten beiden:

    • gets . Natürlich auch scanf mit weg gelassener Längenangabe
    • printf(vom_benutzer_eingegebener_string); Gilt natürlich auch für verwandte Funktionen. Oder diese beiden Punkte zusammen gefasst: Dem Nutzer vertrauen.
      -prüfen, lesen, verarbeiten; anstatt lesen, prüfen, verarbeiten (dieser Punkt ist hart auf der Grenze zwischen No-Go und Fehler)
      -globale Variablen
    • goto
      -Code schreiben (oder gar zusammen kopieren) ohne ihn genau zu verstehen
    • fflush(stdin);
      -C mit C++ mischen
      -Das Rad neu erfinden
      -Einem Rad aus fremder Herstellung zu sehr vertrauen :p

    Die sind natürlich allesamt bieg- und brechbar, sofern man ganz genau weiß, was man wieso tut.



  • 0. C benutzen, wenn man stattdessen C++ benutzen könnte...



  • dachschaden schrieb:

    2. Variablen in for-Headern deklarieren.

    Variablen sollten generell so spät wie möglich deklariert bzw. definiert werden. VS 2013 unterstützt C99 Variablen-Deklarationen.

    http://blogs.msdn.com/b/vcblog/archive/2013/06/28/c-11-14-stl-features-fixes-and-breaking-changes-in-vs-2013.aspx

    dachschaden schrieb:

    4. Cast von malloc (auch von DirkB).

    Das ist in C absolut richtig!

    SeppJ schrieb:

    -globale Variablen

    Man sollte globale Variablen möglichst meiden, aber man kann sie kaum verhindern, weshalb sie nicht in die Kategorie No-Go fallen.

    SeppJ schrieb:

    • goto

    Das stimmt in 98-99% der Fälle, aber gotos, die eigentlich zu Spaghetti-Code führen, können in manchen Fällen zu lesbarerem Code führen. Stichwort verschachtelte Schleifen oder Fehlerbehandlung. gotos sind außerdem effizienter und du findest sie auch im Linux-Kernel.

    L. G.,
    IBV


  • Mod

    IBV schrieb:

    dachschaden schrieb:

    4. Cast von malloc (auch von DirkB).

    Das ist in C absolut richtig!

    Einschränkung: Es ist nicht direkt falsch.

    Man sollte globale Variablen möglichst meiden, aber man kann sie kaum verhindern, weshalb sie nicht in die Kategorie No-Go fallen.
    [...]
    Das stimmt in 98-99% der Fälle, aber gotos, die eigentlich zu Spaghetti-Code führen, können in manchen Fällen zu lesbarerem Code führen. Stichwort verschachtelte Schleifen oder Fehlerbehandlung. gotos sind außerdem effizienter und du findest sie auch im Linux-Kernel.

    Deswegen ja auch die bewusste Einschränkung, dass diese Regeln bieg- und brechbar sind, vorausgesetzt, dass man genau(!) weiß, was man da tut. Globale Variablen, weil es absolut nicht anders geht oder ein goto, das (nach reichlicher Abwägung!) zu besserem Code führt, sind absolut ok. Absolut nicht ok sind globale Variablen oder goto aus Gründen wie "ich habe keine Lust", "ich weiß es nicht besser", "warum nicht?", "hab ich anderswo so gesehen" und ähnlichem.



  • SeppJ schrieb:

    , dass man genau(!) weiß, was man da tut. Globale Variablen, weil es absolut nicht anders geht oder ein goto, das (nach reichlicher Abwägung!) zu besserem Code führt, sind absolut ok.

    Das wäre ein Freibrief für alle, denn niemand wird zugeben, dass er wenig Ahnung und keinen Überblick hat und diesen "Freiraum" nutzen.



  • Wutz schrieb:

    SeppJ schrieb:

    , dass man genau(!) weiß, was man da tut. Globale Variablen, weil es absolut nicht anders geht oder ein goto, das (nach reichlicher Abwägung!) zu besserem Code führt, sind absolut ok.

    Das wäre ein Freibrief für alle, denn niemand wird zugeben, dass er wenig Ahnung und keinen Überblick hat und diesen "Freiraum" nutzen.

    Diese Einschätzung entspricht überhaupt nicht meiner Erfahrung.



  • goto kann man benutzen, muss man aber nicht. Die folgenden drei Programmausschnitte erreichen das gleiche einmal mit Blöcken, einmal mit goto und einmal mit Funktionsaufrufen.

    Man erkennt, dass die horizontale Verschachtelungstiefe der Blöcke mit jeder Ressource zunimmt. Das ist ab drei oder vier Stück nicht unbedingt lesbarer als goto .

    if (allocate_A())
    {
    	if (allocate_B())
    	{
    		if (allocate_C())
    		{
    			puts("Hat alles geklappt");
    			free_C();
    		}
    		free_B();
    	}
    	free_A();
    }
    

    Der Code wächst mit goto nicht in die Breite. Außerdem stehen alle Allokationen an einem Ort und alle Freigaben an einem anderen. Der gesamte Code für den Erfolgsfall steht in der zweiten Spalte, der Code für den Fehlerfall in der ersten (die Labels) und dritten (die goto s).

    if (!allocate_A())
    	{
    		goto fail_A;
    	}
    	if (!allocate_B())
    	{
    		goto fail_B;
    	}
    	if (!allocate_C())
    	{
    		goto fail_C;
    	}
    	puts("Hat alles geklappt");
    	free_C();
    fail_C:
    	free_B();
    fail_B:
    	free_A();
    fail_A:
    

    Die einzelnen Funktionen sind kurz und die Ressourcensituation ist sehr gut überschaubar. Man erkennt sehr leicht, dass Anforderung und Freigabe paarweise und sehr lokal erfolgen. Der Kontrollfluss ist etwas schwieriger zu erkennen, weil globale Symbole und nicht nur lokale Sprungmarken angesprungen werden.

    static void execute_C_step()
    {
    	if (allocate_C())
    	{
    		puts("Hat alles geklappt");
    		free_C();
    	}
    }
    
    static void execute_B_step()
    {
    	if (allocate_B())
    	{
    		execute_C_step();
    		free_B();
    	}
    }
    
    static void execute_A_step()
    {
    	if (allocate_A())
    	{
    		execute_B_step();
    		free_A();
    	}
    }
    

  • Mod

    Da sich die ganzen unregistrierten Trolle hier als ein und dieselbe Person heraus gestellt haben (C0d3rC), habe ich den Thread mal radikal zurecht gestutzt. Im Prinzip war alles nach C0d3rCs erstem Beitrag nur auf seine Provokationen zurück zu führen und den Speicherplatz nicht wert. Da es sonst anscheinend nicht wichtiges mehr zu sagen gab, mache ich auch mal zu, falls er es noch einmal versuchen sollte.


Anmelden zum Antworten