Problem bei Übungsaufgabe



  • Hallo Leute ich habe eine Übungsaufgabe, bei der mir nicht ganz klar ist was passiert und ich hoffe ihr könnt mir helfen

    Der Aufruf des Programms erfolgt auf der Kommandozeile mit den Werten "7900315" und "7948265".

    #include <stdio.h>
    int main(int argc, char *argv[])
    {
        char *s;
        char code[] = "SKIAMXFNLE";
    
        s = *++argv;
        s += argc+2;
        while (*s - '7')
            printf("%c", code[*s-- - '0']);
        return 0;
    }
    

    Meine Gedanken zum Programm:

    1. argc ist ein int-Wert der angibt mit wievielen Werten das Programm auf der Komandozeile aufgerufen wird. Bei dem Aufruf mit dem Programmnamen,"7900315" und "7948265" hat argc den Wert 3.

    s = *++argv;
    

    argv ist ein pointerarray, dass auf den Anfang jedes einzelnen strings auf der Kommandozeile zeigt. Zuerst wird argv um 1 erhöht und dann wird die Anfangsadresse des Strings (bzw. des Char arrays) in s gespeichert.
    Hier zeigt s also dann auf den Wert "7900315".

    s += argc+2;
    

    Hier wird die Adresse um 5 erhöht, d.h. s enhält jetzt nurnoch "15".

    Folgender code ist mir aber absolut unklar:

    while (*s - '7')
            printf("%c", code[*s-- - '0']);
    

    Wäre nett wenn mir jemand helfen könnte, da ich selber nicht draufkomme was hier passiert.

    mit freundlichen Grüßen,

    Marco


  • Mod

    Klingt soweit gut. Vielleicht noch eine Klugscheißerbemerkung, da man dann den Rest besser verstehen kann:

    Hier wird die Adresse um 5 erhöht, d.h. s enhält jetzt nurnoch "15".

    Genauer wäre: s zeigt nun auf die '1'.

    Nun zum Rest:

    while (*s - '7')
    

    Diese Schleife läuft, so lange *s ungleich '7' ist, sprich: So lange wie s auf keine '7' zeigt.

    printf("%c", code[*s-- - '0']);
    

    Es wird ein Zeichen ausgegeben. Dieses Zeichen befindet sich im Array code an der Stelle *s-- - '0' . Dazu später mehr. Erst einmal sei noch gesagt, dass durch das s-- der Wert von s verändert wird. s zeigt nachher auf das Zeichen vor dem Zeichen, auf das es zuletzt gezeigt hat. Also nach dem ersten Durchlauf auf die '3', dann auf die '0', dann auf die andere '0' davor, und so weiter.

    Nun ist auch klar, dass die Schleife letztlich abbrechen wird, da s irgendwann bei der '7' ganz am Anfang landen wird.

    Was ist nun der Wert von *s-- - '0' ? Das -- macht für den eigentlichen Wert des Ausdrucks gar nichts aus, das Dekrement von s geschieht erst nachdem s ausgewertet wurde. Es ist also der gleiche Wert als stünde da nur *s - '0' .

    Nun gibt es in C die Festlegung, dass alle Ziffern geordnet hintereinander in der Zeichentabelle stehen müssen. Sprich: Zuerst kommt '0', dann '1', dann '2', usw. direkt nacheinander. Es steht nicht fest, wo genau sie in der Zeichentabelle stehen müssen. Beim weit verbreiteten ASCII stünde die '0' beispielsweise an Position 48 und entsprechend '1' an Position 49, usw. bis '9' an Position 57. Wenn man also den Wert von '0' von einer anderen Ziffer abzieht, erhält man den Zahlenwert der Ziffer. Beispielsweise ist '5' - '0' == 5 .

    Im ersten Durchlauf, wenn s auf eine '1' zeigt, erhält man also als Index für code den Wert 1. Das ist das Zeichen 'K', welches ausgegeben wird. Im nächsten Durchlauf zeigt s auf eine '3' und man erhält das Zeichen an Position 3 aus code , also ein 'A'. So geht das weiter, bis s auf die '7' zeigt und die Schleife abbricht. Insgesamt erhält man dabei die Zeichenfolge "KASSE". (Da die while-Bedingung geprüft wird, bevor der Schleifenkörper durchlaufen wird, erhält man keine Ausgabe mehr für die '7', die letzte Ausgabe ist daher für die '9'.)



  • Hey danke dir, echt gut erklärt.

    Was ist nun der Wert von *s-- - '0'? Das -- macht für den eigentlichen Wert des Ausdrucks gar nichts aus, das Dekrement von s geschieht erst nachdem s ausgewertet wurde. Es ist also der gleiche Wert als stünde da nur *s - '0'.

    Verstehe ich es richtig, wenn ich sage, dass das "--" nur dafür da ist um die Adresse des arrays für den nächsten Schleifendurchlauf um "1" zu erniedrigen ?

    Zwei Fragen stelle sich mir in diesem Zusammenhang aber noch.

    Wenn ich stattdessen *--S - '0' schreiben würde, dann wird s erst um 1 erniedrigt und dann ausgewertet ?

    Kann ich mithilfe des postfix bzw. präfix operators den Wert auf den der char ptr s zeigt erniedrigen ?

    Also gibt es eine Möglichkeit wenn s gerade auf 4 zeigt ihn mittels ++ auf 5 zu erhöhen ?

    mit freundlichen Grüßen


  • Mod

    Marco12345 schrieb:

    Verstehe ich es richtig, wenn ich sage, dass das "--" nur dafür da ist um die Adresse des arrays für den nächsten Schleifendurchlauf um "1" zu erniedrigen ?

    So ist es.

    Wenn ich stattdessen *--S - '0' schreiben würde, dann wird s erst um 1 erniedrigt und dann ausgewertet ?

    Korrekt. In dem Fall würde ASSEN heraus kommen.

    Kann ich mithilfe des postfix bzw. präfix operators den Wert auf den der char ptr s zeigt erniedrigen ?

    Also gibt es eine Möglichkeit wenn s gerade auf 4 zeigt ihn mittels ++ auf 5 zu erhöhen ?

    Ja, das geht auch. Was du aber beachten musst, ist die Priorität der Operatoren. Wie du in der Tabelle siehst, hat das (Postfix-)Inkrement eine höhere Priorität (Stufe 1) als die Zeigerdereferenzierung (Stufe 2). Um also den dereferenzierten Wert zu inkrementieren, muss die Dereferenzierung zuerst erfolgen. Das heißt, dieser Teilausdruck muss in Klammern gesetzt werden, zum Beispiel (*s)++ .

    Wenn wir hingegen vom Präfix-Inkrement reden, dann sehen wir, dass es die gleiche Priorität wir die Dereferenzierung hat (beide Stufe 2). In dem Fall kommt die Auswertungsreihenfolge auf die Assoziativität (rechte Spalte in der Tabelle) an. Diese ist für Stufe 2 rechts nach links, also was rechts steht, kommt zuerst dran. Bei *++s wird also zuerst s inkrementiert und dann dereferenziert, bei ++*s wird s zuerst dereferenziert und dann das Ergebnis inkrementiert.

    Man kann und darf natürlich trotzdem Klammern setzen. Auch wenn das reihenfolgenabhängige Verhalten, das ich im vorherigen Absatz beschrieben habe, den meisten Leuten intuitiv klar sein dürfte, ist bei *(++s) beziehungsweise ++(*s) noch klarer ersichtlich, was gewünscht ist.

    PS: Du plenkst.



  • Alles klar. Herzlichen dank für deine Hilfe.

    mit freundlichen Grüßen



  • Hab eigentlich gedacht, dass ich soweit bei solchen Zuweisungen weiß was passiert aber dann habe ich folgende Aufgabe versucht:

    int main(void)
    {
    
        char **s, *t;
        char *argv[] = {"prog", "123", "007", "456"};
        int argc = 4;
         char *x[] = {"lsiire_btn", "lneeevxehu", "uiwriiiice", "nezdvfssan"};
    
        t = *(argv +1);
    
        while(--argc)
        {
            while(*t)
            {
                s = x + 4;
    
                while(*s-- - *x)
                    printf("%c", (*s)[*t-'0']);
                printf("-");
                t++;
            }
            printf("\n");
            t = *(argv +1);
        }
    
       return 0;
    
    }
    

    Hab das mal für Codeblocks umgeschrieben, da ich hier nicht über commandozeile auf das Programm zugreifen kann.

    Ich verstehe folgende Zeile nicht:

    s = x + 4;
    

    Mein x array besitzt 4 char ptr. x zeigt zu beginn auf "lsiire_btn". Wenn ich jetzt s erhöhe um 4 zeige ich mit meine **s im speicher einfach außerhalb des Bereichs, da ich x maximal um 3 erhöhen könnte ?

    Laut watches zeigt *s aber immer wieder auf den wert "prog" was für mich bsolut keinen sinn ergibt.

    mit freundlichen Grüßen,


  • Mod

    x+4 ist tatsächlich außerhalb des Arrays x. Es ist undefiniertes Verhalten, was passiert, wenn man auf diesen Ort zugreift. Schließlich könnte da wer weiß was stehen. In diesem Fall steht dort zufällig der Inhalt von argv. Zugegeben ist das nicht ganz zufällig, da die Speicherorte von Variablen normalerweise nicht kreuz und quer im Speicher verteilt sind. Daher ist es nicht so überraschend dort den Inhalt einer anderen lokalen Variablen zu finden. Verlassen kann und darf man sich auf so etwas aber nicht! Theoretisch darf alles Mögliche oder sogar Unmögliche passieren, wenn undefiniertes Verhalten ausgelöst wird.



  • Marco12345 schrieb:

    Hab das mal für Codeblocks umgeschrieben, da ich hier nicht über commandozeile auf das Programm zugreifen kann.

    Zum einen kannst du das Programm selbstverständlich im Konsolenfenster (cmd) laufen lassen.

    Und auch bei Code::Blocks kannst du Argumente einstellen: Im Menü "Project" unter "Set program's arguments..."


  • Mod

    Man sollte vielleicht noch erwähnen:

    1. Das derzeit gezeigte Programm führt selber niemals einen Zugriff auf x+4 aus, es setzt lediglich einen Zeiger, s , hinter das Ende von x . Dieser wird aber dekrementiert, bevor der Zugriff erfolgt. Das setzen eines Zeiger um 1 hinter das Ende eines Arrays ist erlaubt, der Code an sich ist also völlig in Ordnung. Bloß, was dir dein Debugger als Ziel von *s zeigt, ist undefiniert. Bei dir ist es anscheinend argv , wenn ich gleiches bei mir versuche, erhalte ich einen Programmabsturz. Undefiniertes Verhalten eben.

    2. Wenn du den Code denn so umschreibst, wie es DirkB gesagt hat, dann wird entsprechend dein Debugger höchstwahrscheinlich einen ganz anderen Wert als Ziel von *s anzeigen, da argv (welches wiederum auf "prog" zeigt) dann an anderer Stelle im Speicher liegen wird.


Log in to reply