Top 10 NoGos in C



  • Was sind die Top 10 NoGos in C?



  • Die Dinge die hier im Forum am h├Ąufigsten angemault werden sind mMn:

    fflush(stdin);

    cast von malloc

    Etwas anderes als int main() oder int main(void) oder int main(int,char**)

    und das verwenden von Nicht-Standard-Funktionen ­čśâ



    1. int anstelle von size_t verwenden.

    2. Variablen in for-Headern deklarieren.

    3. Falscher main -Header (hat DirkB schon erw├Ąhnt).

    4. Cast von malloc (auch von DirkB).

    5. Arraygr├Â├čen ├╝ber Variablen angeben (VLAs) - also entweder macht man das direkt zur Kompilierzeit ├╝ber irgendeine Art von Konstanten ( #define oder const size_t ), oder man verwendet alloca/_alloca oder malloc .

    6. Adressen auf Speicher auf dem Stack zur├╝ckgeben (siehe J├╝rgen Wolf).

    7. Zweidimensionale Arrays verwenden, wo es keinen Sinn ergibt.

    8. C++ in C einbinden und vice versa.

    9. Windows als Lern- und Entwicklungsplattform einsetzen (die Manpages haben mir damals mehr geholfen als alle Lehrb├╝cher zusammen).

    10. Einen ewig langen Codeblock um eine Bedingung setzen (z.B. if(bla){/*1,5K Zeilen Code*/} und dann am Ende ein einsames else{return 0;} setzen). Das sehe ich noch 20 Jahre in dem Gesch├Ąft arbeitende Profis machen und jedes Mal muss ich das Verlangen unterdr├╝cken, jemanden zu w├╝rgen.

    DirkB schrieb:

    und das verwenden von Nicht-Standard-Funktionen ­čśâ

    Gerade da bin ich ein wenig liberaler. Die Standardfunktionen sind unter anderen Prim├Ąssen entstanden, als in der Realit├Ąt der Fall ist. Ich will nicht eine Bin├Ąrdatei erst als Bin├Ąrdatei einlesen m├╝ssen und mir dann Zeile f├╝r Zeile meinen Speicher f├╝llen lassen. Ich will f├╝nf API-Calls: Gr├Â├če der Datei feststellen, Speicher reservieren, Datei ├Âffnen, Daten einlesen und das in einem Rutsch, und Datei schlie├čen.

    Ich will nicht, dass am Ende ein 0-Byte auftaucht, dass da nicht ist ( fgets/getline ). Ich will auch nicht in einer Schleife solange einlesen m├╝ssen, bis nichts mehr einzulesen gibt (Ausnahme Dateien, die der Kernel f├╝r einen erstellt, siehe /proc -Verzeichnis). Und ich will auch nicht angeben m├╝ssen, wie gro├č die Elemente sind und wie viele davon ich einlesen will ( fread ). Und manchmal, wenn ich eine gro├če Datei einlesen will, will ich die direkt in den Speicher mappen und mir das Kopieren in meinen Userspace sparen.



  • @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.


Log in to reply