Top 10 NoGos in C



  • @dachschaden
    Als jemand der so-gut-wie nie C programmiert muss ich da mal nachfragen...
    ad 2 ... wieso?
    ad 5 ... WIESO? (Was soll alloca statt VLA bringen - fetzt beides wenn man zu viel anfordert)
    ad 8 ... WIESO???
    ad 9 ... Ah, OK, du trollst. Dann erübrigen sich die restlichen Fragen.



  • @hustbaer: m(

    2:

    #include <stdio.h>
    
    int main(int argc,char**argv)
    {
        /*size_t i;*/
        for(size_t i=0;i<argc;i++)
            printf("%lu: %s\n",i,argv[i]);
    
        return 0;
    }
    
    main2.c: In Funktion »main«:
    main2.c:6:2: Fehler: Anfangsdeklarationen in »for«-Schleifen sind nur im C99-Modus erlaubt
    main2.c:6:2: Anmerkung: -std=c99 oder -std=gnu99 verwenden, um den Code zu übersetzen
    

    Und C99 wird noch lange nicht überall unterstützt.

    5:

    Abgesehen davon, dass dir so Code wie dieser:

    #include <stdio.h>
    
    int main(int argc,char**argv)
    {
        /*Deklarationsblock*/
        size_t i;
    
        /*Codeblock*/
        scanf("%lu",&i);
    
        /*Wie, watt? Wieder Deklaration? Noe, wir sind beim Code, du hattest
        **deine Chance.*/
        char bla[i];
        printf("%c\n",bla[0]);
    
        return 0;
    }
    

    abschmiert ... es handelt sich auch wieder um eine C99-Erweiterung.

    8:

    #include <stdio.h>
    #include <iostream>
    
    int main(int argc,char**argv)
    {
        size_t i;
    
        printf("Dann geben Sie mal eine Zahl ein: ");
        std::cin>>i;
        printf("Das haben Sie gut gemacht, die Zahl ist '%lu'\n",i);
    
        return 0;
    }
    

    Weil sich sowas mit einem C-Compiler nicht übersetzen lässt?
    Sorry, aber diese Frage ist an Schwachsinn kaum zu überbieten!

    Den Code kann man zwar mit dem g++ übersetzen, aber das stdio.h ist da noch immer. Kann man durch cstdio ersetzen, aber ein übler Nachgeschmack bleibt.

    9:

    Ich habe zwei Jahre mit Windows C versucht und habe schließlich den ganzen Scheiß weggeworfen. Unter Linux habe ich nicht mal ein Jahr gebraucht damals, um hinter den ganzen Scheiß zu blicken. Ist ja auch verständlich - mit "man getline" kommst du viel schneller an die Dokumentation der Funktion. Und das auch, wenn ich mal offline bin.
    Unter Windows kann ich das zwar auch haben, aber die Offline-Doku fand ich immer mehr als fragwürdig, weil sie manche Themen nicht behandelt hat.



  • 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