C-Kurs, Abend 4 - Lösungen und Fragen


    • skoe
    • 3926 Aufrufe 32 Antworten

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • C-Kurs, Abend 4 - Lösungen und Fragen

      Gerade ist der Abend 4 ist fertig geworden.

      skoe.de/wiki/doku.php?id=ckurs:04-abend4

      Diese Episode ist wieder etwas kürzer und einfacher zu verstehen als der Abend 3.

      Vielen Dank auch wieder an ogd, der diesmal seine C-Kenntnisse beiseite geschoben und mit viel Mühe ein BASIC-Programm zurechtgebastelt hat.

      Fragen und die Lösungen zu den Aufgaben könnt ihr hier posten und diskutieren. Mal sehen, ob diesmal was kommt...

      Für Verbesserungsvorschläge mach ich wieder einen extra Thread auf.

      Viel Spaß!
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      Bau Dir ein eigenes Modul! EasyFlash
    • Hallo zusammen,

      ich muß sagen, es macht mir einen riesen Spaß, mir das Gehirn mal wieder mit C zu verdrehen :-).
      Was mich allerdings wundert, ist, dass total viel über den Kurs geredet wird, anstatt sich hier aktiv zu beteiligen.

      Hier mein Lösungsvorschlag zum Ausgeben einer Zahl mit putchar();
      Für die Funktion abs() hab ich <stdlib.h> eingebunden.

      Quellcode

      1. int gibzahlaus(unsigned long b)
      2. {
      3. char a[7];
      4. int z;
      5. z=0;
      6. while (b>0)
      7. {
      8. a[++z]=48+ b-abs(b/10)*10;
      9. b=abs(b/10);
      10. }
      11. do
      12. {
      13. putchar(a[z--]);
      14. }
      15. while (z);
      16. putchar('\n');
      17. }
      Alles anzeigen

      Vielleicht findet ja noch jemand eine rekursive Version.

      Schönen Gruß, Worf
    • Was mich allerdings wundert, ist, dass total viel über den Kurs geredet wird, anstatt sich hier aktiv zu beteiligen.


      ich für meinen teil halte mich da mit dem posten von lösungen zurück, das wäre albern und würde den anfängern warscheinlich eher wenig bringen :) aber evtl könnte skoe ja nach einer weile das posten von lösungen durch die die es schon können "freigeben" oder so.

      Hier mein Lösungsvorschlag zum Ausgeben einer Zahl mit putchar(); Für die Funktion abs() hab ich <stdlib.h> eingebunden.


      imho sollte so eine lösung ja ausschliesslich das benutzen was bis dahin im kurs bekannt gemacht wurde....sonst machts wenig sinn :)
    • Hallo,

      Was mich allerdings wundert, ist, dass total viel über den Kurs geredet wird, anstatt sich hier aktiv zu beteiligen.
      ich für meinen teil halte mich da mit dem posten von lösungen zurück, das wäre albern und würde den anfängern wahrscheinlich eher wenig bringen

      So ist es. Die Diskutierer waren je meist Leute, die C schon konnten (bzw. einer, der meinte, C zu können).

      Ich hatte ja gehofft, dass auch unter C-(wieder)-lernern eine Diskussion um die Lösungen entsteht. Naja, vielleicht entsteht die ja jetzt hier.

      imho sollte so eine lösung ja ausschliesslich das benutzen was bis dahin im kurs bekannt gemacht wurde....sonst machts wenig sinn :)


      So war es auch gedacht. Und deswegen ein paar Anmerkungen in diese Richtung :)

      Also löblich ist ja erstmal, dass Du versucht hast, eine Lösung zu implementieren, die mehr kann als gefordert. Gerade auf dem C64 ist das aber nicht immer nötig (Speicher...) :)

      Weil wir ja z.B. das Ablegen der Zeichen in einem Array noch nicht behandelt haben, ist die Aufgabe auch ohne lösbar. Ein Tip, was man dazu braucht, stand dabei. Auch die while-Schleifen hatten wir noch nicht. Und die richtige Frage zum abs hat unseen schon gestellt.

      Keiner will und kann Dir verbieten, mehr zu können als im Kurs vorkam. Die Einschränkung auf 1- und 2-stellige Zahlen lässt aber eine viel simplere Lösung zu.

      Vielleicht findet ja noch jemand eine rekursive Version.

      Wenn was in einer einfachen Schleife machbar ist, sollte man darauf verzichten. Und wegen der o.g. Einschränkung braucht man auch nicht mal eine Schleife.

      Mal sehen, ob Dir (oder jemand anderem außer den C-Gurus) eine schlankere Lösung einfällt.

      Gruß,
      Thomas
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      Bau Dir ein eigenes Modul! EasyFlash
    • Hi,

      freut mich dass hier doch noch Stimmung aufkommt.
      Sehr gutes Argument, sich mit posten zurückzuhalten, wenn man's schon kann.
      Eine Diskussion über die verschiedenen Lösungswege, gerade auch mit den Gurus fänd ich toll.

      Also abs() zu nehmen war legitim, bin gespannt, wie es ohne geht.
      ABS() macht das gleiche wie INT() in BASIC, es schneidet die Dezimalstellen ab.

      Hab ich nun die Zahl 1234, dann brauche ich die einzelnen Ziffern um sie als zeichen Darzustellen.
      Im ASCII-Zeichensatz ist bei 48 die '0', bei 49 die '1' und so weiter. Habe ich also eine Zahl x von 0 bis 9, dann kann ich sie einfach mit
      Print chr(48+x)
      oder mit
      putchar(48+x);
      ausgeben.

      Ich nehme also 1234/10 = 123,4 schneide die Dezimalstellen ab abs(123,4) und bekomme 123, nehme das mal 10, dann hab ich 1230.
      Ziehe ich jetzt von
      1234
      1230 ab, dann hab ich die letzte Ziffer, 4, die gebe ich mit putchar aus und behalte die ersten drei Ziffern von der ursprünglichen Zahl. ABS(1234/10)
      123
      Dann hol ich mir wieder die letzte Ziffer und gebe sie aus.

      Dann lese ich letztendlich 4321 auf dem Bildschirm - Mist, die Zahl ist verdreht.
      Daher hab ich etwas weiter ausgeholt und die Ziffern in einen Array geschoben und rückwärts wieder ausgegeben.

      Okay, jetzt ganz einfach für Zahlen von 0-99 ohne ABS().

      Quellcode

      1. int gibzahlaus(int i)
      2. {
      3. if (i<10)
      4. {
      5. putchar(' ');
      6. putchar(48+i);
      7. }
      8. else
      9. {
      10. putchar(48+i/10);
      11. putchar(48+i%10);
      12. }
      13. putchar('\n');
      14. }
      Alles anzeigen

      ... später mehr.
      Gruß, Worf
    • Mein Lösungsvorschlag:

      Quellcode

      1. void gibZahlAus(unsigned char z)
      2. {
      3. if (z > 9) {
      4. putchar(z / 10 + 48);
      5. }
      6. else {
      7. putchar(32);
      8. }
      9. // lieber wäre mir: putchar(z > 9 ? z / 10 + 48 : 32);
      10. // aber das haben wir ja offiziell noch nicht
      11. putchar(z % 10 + 48);
      12. putchar('\n');
      13. }
      Alles anzeigen
      ?STRING TOO LONG ERROR IN 10
      READY.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Ehrlicher ()

    • worf schrieb:

      Also abs() zu nehmen war legitim, bin gespannt, wie es ohne geht.
      ABS() macht das gleiche wie INT() in BASIC, es schneidet die Dezimalstellen ab.

      Nö, abs() in C macht das gleiche wie ABS() in BASIC - es gibt den Absolutwert des Arguments zurück. Eine Funktion die das gleiche macht wie INT() in BASIC gibts zwar schon, aber die liefert auch wieder einen Fliesskommatyp zurück.

      An der Stelle an der du abs() verwendet hast gibt die Division sowieso einen Integer-Wert zurück, da beide Argumente Integer-Werte sind. Hättest du statt "/10" ein "/10.0" hingeschrieben wäre der Rückgabewert tatsächlich eine Fliesskommazahl geworden.

      Quellcode

      1. 10 x=rnd(-1963):fori=1to81:y=rnd(1):next
      2. 20 forj=1to5:printchr$(rnd(1)*16+70);:next
      3. 30 printint(rnd(1)*328)-217

      sd2iec Homepage
    • Ich hätte 'ne Zusatzfrage, wenn Ihr Lust habt:
      Ihr geht in Euren Lösungen davon aus, dass die 0 auf Codeposition 48 liegt.
      Wie könnte man das Programm unabhängig von dieser Annahme machen?

      Und noch eine:
      In der Aufgabe war ja nur die Ausgabe für zweistellige Zahlen gefordert.
      Wie kann man das Programm so ändern, dass auch größere Zahlen nicht zu einer seltsamen Ausgabe führen, sondern einfach die letzten beiden Ziffern ausgeben?
    • @heptasean: Gute Zusatzfrage :)
      @Ehrlicher: Gute Lösung

      Quellcode

      1. putchar(c > 9 ? c / 10 + '0' : ' ');


      Derjenige, der seine C-Kenntnisse tatsächlich ausschließlich aus diesem Kurs hat, wird diese Zeile vermutlich nicht verstehen. Der dort verwendete Operator "bedingung ? wennja : wennnein" macht ungefähr das:

      Quellcode

      1. if (c > 9)
      2. {
      3. result = c / 10 + '0';
      4. }
      5. else
      6. {
      7. result = ' ';
      8. }
      9. putchar(result);


      Da dieser Operator nicht gerade intuitiv ist, habe ich ihn bis jetzt verschwiegen. Aber cool ist er auf alle Fälle. Und das Programm wird vielleicht sogar ein paar Bytes kleiner (habe ich nicht ausprobiert). Die Hilfsvariable "result" (z.B. unsigned char oder char) braucht man in der obigen Lösung nicht.

      Ihr habe es ja inzwischen schon geändert, trotzdem nochmal der Hinweis in Klartext:

      Nicht so gut:

      putchar(48 + 3);
      putchar(32);

      Besser:

      putchar('0' + 3);
      putchar(' ');

      An der Stelle an der du abs() verwendet hast gibt die Division sowieso einen Integer-Wert zurück, da beide Argumente Integer-Werte sind.

      Das kommt vielleicht im Kurs noch nicht so klar rüber. Muss nochmal nachschauen. Wenn man von BASIC kommt, ist die Fließkomma-Idee recht naheliegend.

      Na dann muss ich mich ja endlich mal hinsetzen und die nächste Folge fertig machen. (Und noch ein paar ausstehende Hinweise einbauen.)

      P.S. Habt Ihr denn mal darauf geschaut, wie groß das neue Programm im Vergleich zu dem mit printf wird?
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      Bau Dir ein eigenes Modul! EasyFlash
    • Ehrlicher schrieb:

      Quellcode

      1. void gibZahlAus(unsigned char z)
      2. {
      3. unsigned char c = z % 100;
      4. putchar(c > 9 ? c / 10 + '0' : ' ');
      5. putchar(c % 10 + '0');
      6. putchar('\n');
      7. }
      Schließe mich skoe an: Gute Lösung!

      Meine war ein ganzes Stück länger, weil ich bei den putchars auch noch eine (überflüssige) Hilfsvariable hatte.

      Du kennst schon eine Sprache aus der C-Familie, oder? Auf den ?:-Operator kommt man ja nicht so ohne weiteres. ...

      Übrigens kann man die Hilfsvariable c auch noch einsparen (auf dem CeVi kommt's ja gelegentlich auf jedes Byte an. ;) ). Einfach z weiterverwenden.

      Je nachdem, wie „teuer“ die Ausführung von % ist (und ob es darauf ankommt), könnte man um das "c = z % 100" noch eine Bedingung rumpacken.

      Wenn Ihr ein bisschen Assembler könnt, könnt Ihr die Ergebnisse der verschiedenen Varianten ja mal vergleichen (siehe „Dem Compiler auf die Finger schauen“).
    • sauhund schrieb:

      Den ?-Operator sollte man eh nicht verwenden.
      ausser man will möglichst unleserlichen code der auch nicht besonders schnell ist =)
      Öhm, in diesem Fall produziert der ?-Operator das IMHO „bessere“ Assembler-Ergebnis. Also so kategorisch würde ich ihn nicht ablehnen.

      Das mit dem „unleserlich“ ist auch relativ. In diesem Beispiel hat er keine Auswirkung auf das spätere Ergebnis und macht nur eine Fallunterscheidung ob Space oder ‚1‘ bis ‚9‘ (statt ‚0‘ bis ‚9‘) ausgegeben wird. An solchen kosmetischen Stellen ist er, finde ich, sogar übersichtlicher als die if-else-Alternative, die für eine simple Sache („Ich will keine führenden Nullen, sondern Spaces.“) viel Code braucht.

      Ihr habt natürlich recht, wenn man ihn an für den weiteren Verlauf essentiellen Stellen (am besten noch kombiniert mit Zuweisungsoperatoren mit möglichst vielen Seiteneffekten) verwendet.

      Sauhund, hast Du ein Beispiel, wo der Operator wirklich langsameren Code als der if-else-Ersatz produziert?
    • Sauhund, hast Du ein Beispiel, wo der Operator wirklich langsameren Code als der if-else-Ersatz produziert?

      für cc65 wüsste ich jetzt aus dem stehgreif nichts.... vermutlich aber - so wie bei den meissten andren compilern - immer dann wenn es nicht wie hier um einen praktisch trivialen ausdruck geht.
    • C/C++ bietet sehr viele Möglichkeiten, die man nur in besonderen Ausnahmefällen verwenden sollte. Der ?-Operator ist eine davon. Und ob der ?-Operator schneller oder langsamer als gewöhnliches if-then-else ist hängt vom Optimierer ab. In den Fällen wo ich nachgesehen hatte, wurde es genau so wie if-then-else compiliert.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Fröhn ()

    • schlimm wirds halt in der regel wenn leute anfangen lustige macros damit zu bauen wo sie mal lieber inline funktionen genommen hätten :)

      davon ab scheint ein qualitätsmerkmal speziell von c++ programmen zu sein das möglichst in jedem sourcefile alle c++ features benutzt werden =P
    • sauhund schrieb:

      schlimm wirds halt in der regel wenn leute anfangen lustige macros damit zu bauen wo sie mal lieber inline funktionen genommen hätten :)

      Es gibt bei manchen Leuten eine starke Abneigung gegenüber static inline-Funktionen in Headern, obwohl umfangreiche #define (inkl. do{}while(0)-Blockbildung und anderen Späßen) akzeptiert werden... Ok, ich bin da auch nicht komplett unschuldig.

      Quellcode

      1. 10 x=rnd(-1963):fori=1to81:y=rnd(1):next
      2. 20 forj=1to5:printchr$(rnd(1)*16+70);:next
      3. 30 printint(rnd(1)*328)-217

      sd2iec Homepage