C While-Schleife wird nicht verlassen! Programmieranfänger sagt "Hallo welt" :-)



  • Hallo!

    ich bin neu hier und das ist mein erster Beitrag in diesem Forum. Alle Forenteilnehmer möchte ich recht herzlich grüßen!

    Seit ein paar Wochen versuche ich C zu lernen, bin also noch blutiger Anfänger.
    Mein erstes "richtiges Projekt" ist ein Programm, das die Collatz-Folge berechnen soll. Soweit funktioniert es
    eigentlich auch. Es sollte aber in der main()-Funktion weiter gehen, sobald der *pointer (bzw. der Integer zahl, auf den der *pointer zeigt) bei 1 angekommen ist.

    Die while-schleife in der start()-Funktion wird aber nicht verlassen. Es wird stattdessen immer weiter gerechnet (also 1, 4, 2, 1...)
    while(*zeiger > 1) bedeutet doch "mache was in { } steht, solange *zeiger größer als 1 ist!?

    Ich habe dann ein zweites, einfaches Test-Programm geschrieben, das im Prinzip genau so aufgebaut ist, wie das Collatz-Programm. Dieses funktioniert allerdings so wie es soll! ( while(a < 5000) ) Sobald a = 5000 ist, gehts in die main().

    Was habe ich beim Collatz-Programm falsch gemacht? Warum tut das Programm nicht, was in der while-Schleife vorgegeben ist?

    Vielen Dank schon mal!

    P.s. Windows 8.1, CodeBlocks 20.03

    Hier das Collatz-Folge Programm (danach das Test- programm)

    #include <stdio.h>
    #include <stdlib.h>
    int *zeiger;
    
    start()
    {
    
    while(*zeiger > 1)
    {
            if(*zeiger % 2 == 0)
            {
                gerade();
            }
            if(*zeiger % 2 != 0)
            {
               ungerade();
            }
    }
    }
    
    gerade()
    {
        *zeiger = *zeiger / 2;
        printf("%d\n", *zeiger);
        sleep(1);
    
    }
    
    ungerade()
    {
        *zeiger = 3 * *zeiger + 1;
        printf("%d\n", *zeiger);
        sleep(1);
    
    }
    
    main()
    
    {
        int zahl;
        zeiger = &zahl;
        printf("gebe eine zahl ein\n");
        scanf("%d", &zahl);
        start();
    printf("142\n");
    
    }
    
    
    

    Test-Programm

    #include <stdio.h>
    
    int a = 10;
    
    start()
    {
    
    
    while(a < 5000)
    {
        ++a;
        printf("%d", a);
        weiter();
    }
    }
    weiter()
    {
        a * 2;
        printf("%d", a);
        weiterb();
    }
    
    weiterb()
    {
        a + 1;
        printf("%d", a);
    }
    
    main()
    {
        start();
        printf("test\n");
    }
    
    


  • @68000er In Zeile 14 sollte ein else stehen.



  • @68000er Aber noch was anderes:

    Wo lernt man so einen schrecklichen Stil?



    • globale Variablen sind Schrott

    • benutze stattdessen immer Parameter, dafür sind sie da

    • sleep ist kein Standard

    • implicit int ist kein C99

    • du setzt zahl in gerade() und fragst anschließend den dort gesetzten Wert wieder ab, anstatt den vorher geltenden abzufragen

    https://onlinegdb.com/S1esMiv5U



  • @68000er Im Testprogramm änderst du in den weiter-Funktionen den Wert von a nicht.

    Das sollte eine Compilerwarnung geben - wenn nicht, ändere die Compilereinstellung.



  • Hallo,

    vielen Dank für eure Antworten!

    @DirkB
    else:
    Das Programm wurde entsprechend geändert!

    Stil:
    Was den Stil angeht (du meinst damit die Einrückungen, oder?): das habe ich nirgends gelernt. Wie gesagt,
    bin ich totaler Anfänger. Ein guter Stil ist doch, wenn das Programm gut lesbar ist!? Ich dachte mir, dass es so sehr gut lesbar ist. Man erkennt auf den ersten Blick die verschiedenen Funktionen. Aber vielleicht täusche ich mich da auch. Bin für konstruktive Kritik offen. Warum bist Du der Meinung dass mein Stil schrecklich ist?

    Testprogramm:
    Es gibt sogar eine ganze Menge Kompilierwarnungen. Ehrlich gesagt, ignoriere ich das, solange keine Fehlermeldung erscheint... Sollte ich immer darauf achten, dass keine Warnungen angezeigt werde, auch wenn das Programm kompiliert und tut was es soll? Ernst gemeinte frage.

    @Wutz
    globale Variablen:
    In einem Youtube-Tutorial, das ich ziemlich gut finde, wurden globale Variablen erklärt. Dass sie "schrott" wären, wurde nicht gesagt. Warum sind globale Variablen schrott?

    Parameter:
    Habe das Programm nun so geändert, dass *zeiger nicht mehr eine globale Variable ist, sondern in der start()-Funktion definiert wird. Meinst Du das mit Parameter?

    sleep:
    Ich war mir ziemlich sicher dass ich irgendwo gelesen habe, dass sleep in der stdlib.h drin ist. Da hab ich mich wohl geirrt... seltsamerweise funktioniert sleep auch ohne dass ich irgendeine header-datei einbinde. Übrigens auch printf bzw. das komplette Programm läuft durch, ohne dass ich am anfang auch nur die stdio.h einbinde. Falsche Einstellungen? Habe nichts an den Einstellungen verändert. Nur ein neues Projekt eröffnet als C Konsolen-Anwendung. 🤔

    implicit int
    Puh! Sorry, hab keinen Schimmer was du meinst. Muss mich da noch einlesen was das sein soll!

    Mal generell: man sollte besser darauf achten, dass man standard Headerdateien einbindet, und z.b. windows.h und conio.h meiden, damit das Programm auch für andere Betriebsysteme portierbar bleibt, oder?
    Habe auch schon auf meinem Raspberry Pi 4 mit gcc kompiliert. Ist aber ziemlich mühselig mit nano und der Konsole. Soll ich mich lieber daran gewöhnen und generell mit Linux und gcc Programmieren statt mit Windows und CodeBlocks, oder ist das absolut egal für einen Neuling? 🤔

    Überarbeitetes Programm
    Hier nun das überarbeitete Programm. Ich habe die beiden Funktionen gerade() und ungerade() gelöscht, und das was sie tun sollen, in die if und else if schleifen der while-Schleife gepackt.
    Jetzt funktioniert es auch so wie ich wollte. Nachdem die while-Schleife in der start()-Funktion abgearbeitet ist, wird in der main() weiter gemacht.

    Vielen Dank noch mal!
    (Ich habe sleep immer noch drin. Solange ich nicht weiss, wie ich die Ausgabe der Zahlen verzögert anzeigen lassen kann.)

    start(int *zeiger)
    {
    while(*zeiger > 1)
    {
            if(*zeiger % 2 == 0)
            {
                *zeiger = *zeiger / 2;
                printf("%d\n", *zeiger);
                sleep(1);
            }
            else if(*zeiger % 2 != 0)
            {
               *zeiger = 3 * *zeiger + 1;
               printf("%d\n", *zeiger);
               sleep(1);
            }
    }
    }
    main()
    {
    int zahl;
    printf ("bitte zahl eingeben:\n");
    scanf("%d", &zahl);
    start(&zahl);
    printf ("421\n");
    }
    


  • Als Anfänger behandle Warnungen immer wie Fehler (es gibt auch entsprechende Compileroptionen dafür), da du davon ausgehen kannst, daß der Code zwar kompiliert, aber nicht das macht, was du erwartest!



  • @68000er

    else:

    if(*zeiger % 2 == 0)
    {
    }
    else if(*zeiger % 2 != 0)

    Das if im else-Zweig ist sinnlos. Was soll da denn anders sein. Wenn die Zahl nicht gerade ist, kann sie nur ungerade sein.

    Stil:
    Im Prinzip das, was Wutz aufgezählt hat.

    Compilerwarnungen (Testprogramm)/ implicit int:
    C erlaubt - aus historischen Gründen - sehr viel Mist.
    Dazu gehört implicit int, also die Annahme, das etwas vom Typ int ist, wenn es nicht anders deklariert wurde.
    Dazu gehört u.A. der Rückgabetyp einer Funktion oder das benutzen einer Funktion Standardfunktion ohne Header.
    Das ist im C-Standard seit 1999 nicht mehr erlaubt.

    Teilweise sind solche Sachen auch Undefiniertes Verhalten (ub) - und da darf der Compiler alles machen: Platte Formatieren, Kätzchen töten oder den 3. Weltkrieg anfangen.

    Behandele Warnungen wie Fehler und beseitige deren Ursache.

    globale Variablen:
    Du verlierst sehr leicht den Überblick, wann und wo die Variable geändert wurde.
    Deine Anfrage hier ist das beste Beispiel dafür.

    Parameter:
    Warum überhaupt ein Zeiger?
    Du hast eine Funktion. Die hat einen Rückgabewert.

    Überarbeitetes Programm:
    Es gibt keine if-Schleifen, denn da wird nichts wiederholt. ifist eine Anweisung.

    Auchmain ist eine Funktion und hat einen Rückgabewert und auch Parameter.



  • @DirkB sagte in [C While-Schleife wird nicht verlassen! Programmieranfänger sagt "Hallo welt"

    C erlaubt - aus historischen Gründen - sehr viel Mist.
    Dazu gehört implicit int, also die Annahme, das etwas vom Typ int ist, wenn es nicht anders deklariert wurde.
    Dazu gehört u.A. der Rückgabetyp einer Funktion oder das benutzen einer Funktion Standardfunktion ohne Header.
    Das ist im C-Standard seit 1999 nicht mehr erlaubt.

    Und es ist seit der ANSI Norm 1989 bereits als veraltet deklariert, sprich man sollte es seit damals bereits unterlassen. Das ist noch K&R C, und das ist wirklich alt. Wo treibt man solche Literatur auf? Mein wirklich altes K&R ANSI C Lehrbuch, beschreibt schon wie man es besser macht. Obwohl das Buch aus heutiger Sicht etliche Fehler enthält.



  • @68000er sagte in C While-Schleife wird nicht verlassen! Programmieranfänger sagt "Hallo welt" 🙂:

    Stil:
    Was den Stil angeht (du meinst damit die Einrückungen, oder?): das habe ich nirgends gelernt.

    Das sollte aber in deinen Lehrmittel gezeigt - und wenn es gut ist auch begründet - werden.

    Wie gesagt,
    bin ich totaler Anfänger. Ein guter Stil ist doch, wenn das Programm gut lesbar ist!? Ich dachte mir, dass es so sehr gut lesbar ist. Man erkennt auf den ersten Blick die verschiedenen Funktionen. Aber vielleicht täusche ich mich da auch.

    Du siehst nicht richtig, wo die Funktionen enden, da auf er Ebene zu viel steht.
    Im Prinzip erfolgt mit jeder { eine Einrückung und mit jeder } eine Ausrückung.
    Ob vor oder nach der Klammer, ob die Klammer nach der Anweisung in der nächsten Zeile steht und die Anzahl der Leerzeichen spielt erstmal keine Rolle, sollte aber konstant sein.

    Schau mal unter https://de.wikipedia.org/wiki/Einrückungsstil was dir gefällt.



  • @68000er

    In einem Youtube-Tutorial, das ich ziemlich gut finde, wurden globale Variablen erklärt. Dass sie "schrott" wären, wurde nicht gesagt. Warum sind globale Variablen schrott?

    Globale Variablen führen gerne zu schlechtem Design und zu vielen Seiteneffekten.

    Um dir dies zu erklären stell dir mal vor ich wäre ein Programmieranfänger und müsste deinen ersten Beitrag erweitern.

    Meine ersten Fragen wären:
    Was machen Funktion weiter(), weiterb() und start()?
    Wie füttere ich die Funktionen und welche Rückgaben haben diese?

    Und nun bekomme die Aufgabe anstatt über globale Variable a zu iterieren über eine zweite globale Variable namens b zu iterieren.

    Und da ich ein schlechter Programmierer bin, kopiere ich die Funktionen start(), weiter() und weiterb(), benenne diese in start_2(), weiter_2() und weiterb_2() um und ersetze jedes Vorkommen von a durch b. Dummerweise passiert mir ein Fehler und an einer Stelle benutze ich noch globale Variable a anstatt b. Und nun stell dir vor jede Funktion wäre mindestens 500 Zeilen groß.

    Dann habe ich gelitten.

    -------‐------

    Mein Beispiel ist natürlich voll mit Designfehlern, angefangen bei der unnützen Verwendung von globalen Variablen in Verbindung mit Funktionen mit schlechtem Design ohne Parameter und Rückgabewerte, riesigen Funktionen und dem Copy Paste Modify Ansatz.

    Aber ich habe schon alles gesehen.



  • @68000er sagte in C While-Schleife wird nicht verlassen! Programmieranfänger sagt "Hallo welt" 🙂:

    In einem Youtube-Tutorial, das ich ziemlich gut finde, wurden globale Variablen erklärt

    Du kannst als Anfänger nicht beurteilen, ob ein Tutorial gut oder schlecht ist.
    Die meisten sind schlecht.



  • @Th69
    Warnungen, Fehler
    Ok. Ich werde versuchen darauf zu achten.

    @DirkB
    Globale Variablen
    Habe schon einige Quelltexte gesehen, die globale Variablen hatten. Aber gut, nur weil etwas im Internet veröffentlicht wurde, muss es ja nicht richtig sein.
    Gibt es denn überhaupt irgendeinen Fall, in dem man globale Variablen nehmen sollte? Warum wurde das überhaupt in die Sprache integriert wenn man es meiden soll?
    Lehrmittel
    Meine "Lehrmittel" sind nur das K&R Progamming Language und einige Youtube-Tutorials. Wobei ich zugeben muss, dass es mir mehr liegt Videos zum Thema zu schauen als im Buch zu lesen. Selbstverständlich wird alles gleich selbst eingetippt und ausprobiert. Lass mich raten: falsche Vorgehensweise? Werde mich wohl doch mehr mit dem Buch beschäftigen.
    Stil
    Muss mich wirklich mehr damit beschäftigen. Finde die Stile auf der Wiki-Seite sehr verwirrend. Da fehlt mir wohl noch das Auge dafür.

    Alles andere
    Ich muss wohl noch sehr viel lernen!

    @john-0
    K&R
    Also, jetzt bin ich ehrlich gesagt komplett verwirrt! Überall liest man, "The C Programming Language" von Kernighan und Ritchie, sei auf jeden Fall empfehlenswert, wenn man C lernen will. Sozusagen die Bibel für (angehende) C-Programmierer. Was ja auch logisch ist, da K&R ja auch die Erfinder der Sprache sind.

    Du schreibst jetzt aber , dass das Buch "aus heutiger Sicht eltliche Fehler enthält"?! Tut mir leid, ich komme da echt nicht mehr mit. Das K&R C-Buch (zweite Ausgabe) ist das einzige Buch das ich zum Thema C besitze, habe es mir auch nur deswegen gekauft, weil es überall uneingeschränkt empfohlen wird. Von Büchern auf deutsch, die sich mit C beschäftigen, soll man ja die Finger lassen. Das kann ich übrigens überhaupt nicht verstehen, dass es in der gesamten Zeit, in der die Sprache existiert, kein einziges vernünftiges Buch auf deutsch erschienen sein soll, das man ohne einschränkungen empfehlen kann.

    Was meinst Du überhaupt mit "aus heutiger sicht etliche Fehler"?? Meinst du, die Standards die danach erschienen sind? Dachte, die verschiedenen Standards (C89, C90, C99...) würden sich nicht grundlegend unterscheiden.

    @Quiche-Lorraine
    Fand es bis jetzt eigentlich ganz bequem mit den globalen Variablen. Man definiert am Anfang und muss sich dann nicht mehr darum kümmern. Da muss ich wohl komplett umdenken.

    @Wutz
    Haha! Du machst mir Spaß! Natürlich kann ich das nicht beurteilen als Anfänger, wie auch? 😄
    Aber, wie kann ich denn wenigstens erahnen, welches Tutorial gut oder schlecht ist? Fühle mich wirklich etwas verloren. Bücher sind Mist. Tutorials sind Mist. Selbst Leute die es gelernt haben, an der Uni und beruflich Programmieren, können schlechte Programmierer sein und Mist bauen...

    Würde mich wirklich gerne an einen Leitfaden halten, mit dem Wissen, dass das was ich da lerne so auch richtig ist, und mir nicht erst den Kopf zermartern müssen, was denn nun gute Bücher, Tutorials etc. sind und was nicht.

    Ich will ja nicht jammern, jammere aber trotzdem ein wenig...



  • @68000er C ist eine Sprache für Profis. Die Entwickler wollten für sich eine Erleichterung haben.
    Daher sind auch Dinge möglich, die für einen Anfänger „tötlich“ sind.

    Das K&R beschreibt die Sprache. Es ist zum lernen der Sprache, nicht zum programmieren lernen (geht aber auch) gedacht.

    Das Buch enthält Fehler, die sind aber dokumentiert.

    Auch die Standard Bibliothek ist, gerade beim Umgang mit Arrays, ein Minenfeld.

    Das soll dich nicht entmutigen. C ist ziemlich einfach aufgebaut, da es wenig Ausnahmen gibt.



  • @68000er sagte in C While-Schleife wird nicht verlassen! Programmieranfänger sagt "Hallo welt" 🙂:

    Du schreibst jetzt aber , dass das Buch "aus heutiger Sicht eltliche Fehler enthält"?!

    john-0 erzählt wie üblich Unsinn, basierend auf aufgeschnapptem Halbwissen.
    K&R2 enthält nicht etliche sondern einige Fehler, aber - im Gegensatz zu den meisten anderen - keine grundlegenden, und dafür gibts ein errata:
    Errata for The C Programming Language, Second Edition
    Die deutsche Version ist (inhaltlich) nicht schlechter als die englische, das ist verbreiteter Aberglaube. K&R2 ist mehr als Referenz für C zu verstehen denn als Lehrbuch.
    Es gibt noch einige kompetente Links, C-FAQ ebenfalls mit errata, und auch noch C Primer Plus, S.Prata, 5.Edition (da gibts auch ein (halblegales?) PDF zum Download).
    Youtubes kenne ich kein einziges Brauchbares.
    C ist eine Sprache von Profis für Profis, und als solche ist sie eben nicht als Lernsprache konzipiert, wie andere von Theoretikern entwickelte.
    C lernt man ausschließlich in der Praxis - irgendwann fällt dann auch mal der Groschen beim Verständnis von Zeigern - aber eben nicht nach kurzfristigem Konsum von Buchautoren, Hochschullehrern und Tutorialschreiberlingen.
    Vorsicht vor allen Quellen wo C / C++ draufsteht; die sind grundsätzlich Schrott - gilt auch für deine Kurse an der Uni und für C++ler, die sich an der Verwendung von C Zeigern versuchen.



  • @68000er sagte in [C While-Schleife wird nicht verlassen! Programmieranfänger sagt "Hallo welt"

    @john-0
    K&R
    Also, jetzt bin ich ehrlich gesagt komplett verwirrt! Überall liest man, "The C Programming Language" von Kernighan und Ritchie, sei auf jeden Fall empfehlenswert, wenn man C lernen will. Sozusagen die Bibel für (angehende) C-Programmierer. Was ja auch logisch ist, da K&R ja auch die Erfinder der Sprache sind.

    Du schreibst jetzt aber , dass das Buch "aus heutiger Sicht eltliche Fehler enthält"?! Tut mir leid, ich komme da echt nicht mehr mit. Das K&R C-Buch (zweite Ausgabe) ist das einzige Buch das ich zum Thema C besitze, habe es mir auch nur deswegen gekauft, weil es überall uneingeschränkt empfohlen wird. Von Büchern auf deutsch, die sich mit C beschäftigen, soll man ja die Finger lassen. Das kann ich übrigens überhaupt nicht verstehen, dass es in der gesamten Zeit, in der die Sprache existiert, kein einziges vernünftiges Buch auf deutsch erschienen sein soll, das man ohne einschränkungen empfehlen kann.

    Was meinst Du überhaupt mit "aus heutiger sicht etliche Fehler"?? Meinst du, die Standards die danach erschienen sind? Dachte, die verschiedenen Standards (C89, C90, C99...) würden sich nicht grundlegend unterscheiden.

    Fangen wir am Anfang an. Die beiden Schöpfer der Sprache Kernighan und Richtie haben eine neue Sprache geschaffen die auf B und BCPL basiert. Der Hauptgrund war UNIX. UNIX Version 4 wurde in C neu implementiert. Zwar war dieser Code noch PDP-11 abhängig, aber die Basis für eine Portierung auf die Interdata 8/32 (1978) war gelegt. Seit damals ist UNIX dank C sehr portabel. Diese erste Version von C wird im K&R C Buch von 1978 beschrieben. Die Sprachrevisionen unterscheiden man daher als K&R C (vor der ANSI Norm 1989), die ANSI Norm 1989 (die 1990 zur ISO Norm wurde), die ISO Norm 1999, 2011 und 2018. Die Korrekturen dazwischen lasse ich mal aus, das kann man bei ISO nachlesen. Es gab einen Bruch von K&R C hinzu ANSI C, die Sprache hat sich da in einigen Punkten deutlich geändert, und zwar so, dass alte K&R C Programme keinen gültigen ANSI C Programme mehr sind. Das Problem existiert bei den ISO Normen dann nicht mehr. D.h. ein ISO C1990 Programm lässt sich bis auf potentielle Bezeichnerkollisionen mit Schlüsselwörter, immer noch mit einem aktuellen Compiler korrekt übersetzen.

    Die Auflage des K&R C Buches von 1990 bezieht sich zwar auf ANSI C 1989 , aber weist etliche Abweichungen von der Norm auf. Wenn man diese kennt, kann man das Buch noch immer verwenden, obwohl es nicht vollständig ist, weil aktuelles C doch etliche Sprachkonstrukte mehr aufweist als ANSI C. Nur als Anfänger weist Du nun einmal nicht von diesen Abweichungen. Als ich damals C lernte war das Buch noch recht neu, und es gab kaum Alternativen am Markt. Die Änderungen kamen damals nach und nach über POSIX und UNIX (*) in Umlauf, und wurde dann in die C ISO Norm eingebaut. Man hat sich darüber über die Neuerungen immer wieder informieren können.

    (*) UNIX war die erste Plattform, bei der das 64Bit Programmiermodell eingeführt wurde. Aus diesem Grund musste man damals schon aufpassen, dass man die Programme 64Bit clean schrieb, und das geht nicht mit den Datentypen, wie sie in K&R vermittelt werden. Das Begleitbuch zur Single UNIX Specification V2 enthält deshalb ein extra Kapitel wie man korrekt Datentypen im 64Bit Modus verwendet. Einige hier im Forum halten das noch immer für überflüssig.

    Was mir sofort auffällt, wenn ich das alte Buch aus dem Regal nehme, die main Funktion wird nie als int main() oder int main(int argc, char *argv[]) definiert, sondern immer ohne Rückgabetyp definiert. Ich dachte, dass das in der ANSI Ausgabe korrigiert sei, leider ist dem nicht so. Ich hatte zuletzt die ältere Ausgabe von 1978 als Nachschlagewerk benutzt, da sie so im Büro herumlag. D.h. die ANSI Ausgabe beschreibt C irgend wo zwischen K&R C und ANSI C, und das ist nicht gut.



  • @john-0 sagte in C While-Schleife wird nicht verlassen! Programmieranfänger sagt "Hallo welt" 🙂:

    Die Änderungen kamen damals nach und nach über POSIX und UNIX (*) in Umlauf, und wurde dann in die C ISO Norm eingebaut.

    john-0 erzählt wie üblich Unsinn, basierend auf aufgeschnapptem Halbwissen.



  • @Wutz sagte in [C While-Schleife wird nicht verlassen! Programmieranfänger sagt "Hallo welt" :-)]

    john-0 erzählt wie üblich Unsinn, basierend auf aufgeschnapptem Halbwissen.

    Ich kann wenigstens 64Bit saubere C und C++ Programme schreiben, daran scheiterst du ja elementar, und weil kein Wissen bei dir vorhanden ist, wir das mit schlechtem Benehmen kompensiert. Ein Dirty Harry Bildchen und der Nutzername "Wutz" sind keine Berechtigung sich daneben zu benehmen.


Anmelden zum Antworten