Notes 2

Sie können diese Seite ausdrucken oder mit einem PDF-Drucker in ein PDF umwandeln, um Ihre eigenen Notizen hinzuzufügen.

Sie können bereits C? Schauen Sie sich doch einmal die Shorts zu dieser Vorlesung an. Gerade in den „Drei Extra-Minuten“ gibt es ein paar anspruchsvollere Inhalte.

Willkommen!

  • In der letzten Sitzung haben wir uns mit C, einer textbasierten Programmiersprache, beschäftigt.
  • In dieser Woche werden wir einen tieferen Blick auf zusätzliche Bausteine werfen, die unser Ziel unterstützen, mehr über das Programmieren von Grund auf zu lernen.
  • Grundsätzlich geht es – wie bereits mehrfach erwähnt – in diesem Kurs neben den Grundlagen der Programmierung auch um das Lösen von Problemen. Dementsprechend werden wir uns auch weiter darauf konzentrieren, wie man an Informatikprobleme herangeht.

Kompilieren

  • Unter Verschlüsselung versteht man das Verbergen von Klartext vor neugierigen Augen. Die Entschlüsselung besteht darin, einen verschlüsselten Text wieder in eine für Menschen lesbare Form zu bringen.

  • Ein verschlüsselter Text kann wie folgt aussehen:

    Verschlüsselung
    Verschlüsselung

  • Erinnern Sie sich, dass Sie letzte Woche etwas über einen Compiler gelernt haben, ein spezialisiertes Computerprogramm, das Quellcode in Maschinencode umwandelt, der von einem Computer verstanden werden kann.

  • Sie könnten zum Beispiel ein Computerprogramm haben, das wie folgt aussieht:

    #include <stdio.h>
    
    int main(void)
    {
        printf("Hello, world\n");
    }
  • Ein Compiler wandelt den obigen Code in den folgenden Maschinencode um:

    Maschinencode
    Maschinencode

  • VS Code, die Programmierumgebung, die wir Ihnen zur Verfügung stellen, verwendet einen Compiler namens “clang” (übliche Aussprache: “Kläng”); der Name kommt von „c language“.

  • Wenn Sie make hello eingeben, wird ein Befehl ausgeführt, der clang ausführt, um eine Ausgabedatei zu erstellen, die Sie als Benutzer ausführen können.

  • VS Code ist so vorprogrammiert, dass make zahlreiche Befehlszeilenargumente zusammen mit clang ausführt, damit Sie es bequemer haben.

  • Betrachten Sie den folgenden Code:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        string name = get_string("What's your name? ");
        printf("hello, %s\n", name);
    }
  • Sie können versuchen, in das Terminalfenster einzugeben: clang -o hello hello.c. Sie werden eine Fehlermeldung erhalten, die besagt, dass clang nicht weiß, wo die Bibliothek cs50.h zu finden ist.

  • Versuchen Sie erneut, diesen Code zu kompilieren, indem Sie den folgenden Befehl im Terminalfenster ausführen: clang -o hello hello.c -lcs50. Dies informiert den Compiler, dass er die Bibliothek “cs50” bei der Erstellung der Ausgabedatei verwenden soll.

  • Wenn Sie den Befehl im Terminalfenster ./hello ausführen, wird Ihr Programm wie vorgesehen laufen.

  • Die obigen Ausführungen dienen der Veranschaulichung, damit Sie den Prozess und das Konzept des Kompilierens von Code besser verstehen können. Die Verwendung von make in CS50 ist völlig in Ordnung – es bringt Ihnen keinen Vorteil, clang direkt aufzurufen.

  • Das Kompilieren umfasst mehrere Schritte:

    • Erstens werden bei der Vorverarbeitung (Preprocessing) die Header-Dateien in Ihrem Code, die durch ein # gekennzeichnet sind (z.B. #include <cs50.h>), in Ihre Datei eingefügt. Bei diesem Schritt wird also der Code aus cs50.h in Ihr Programm kopiert. Wenn Ihr Code #include <stdio.h> enthält, wird der Code, der in stdio.h irgendwo auf Ihrem Rechner hinterlegt ist, in Ihr Programm kopiert. Dieser Schritt kann wie folgt veranschaulicht werden:

      string get_string(string prompt);
      int printf(string format, ...);
      
      int main(void)
      {
          string name = get_string("What's your name? ");
          printf("hello, %s\n", name);
      }
    • Der zweite Schritt ist das Kompilieren, bei dem Ihr Programm in Assemblercode umgewandelt wird. Assemblycode sieht z.B. wie folgt aus:

      Kompilieren
      Kompilieren

    • Drittens wird der Assemblycode beim Assemblieren vom Compiler in Maschinencode umgewandelt. Dann sieht der Code so aus:

      assembling
      assembling

    • Schließlich wird während des linking-Schrittes der Code aus den von Ihnen eingebundenen Bibliotheken auch in Maschinencode umgewandelt und mit Ihrem Code kombiniert. Die endgültige ausführbare Datei wird dann ausgegeben.

      linking
      linking

Fehlersuche

  • Jeder macht beim Programmieren Fehler.

  • Betrachten Sie das folgende Bild von letzter Woche:

    mario
    mario

  • Betrachten Sie außerdem den folgenden Code, in den absichtlich ein Fehler eingefügt wurde:

    #include <stdio.h>
    
    int main(void)
    {
        for (int i = 0; i <= 3; i++)
        {
            printf("#\n");
        }
    }
  • Geben Sie code buggy0.c in das Terminalfenster ein und schreiben Sie den obigen Code.

  • Wenn Sie diesen Code ausführen, erscheinen vier Steine statt der geplanten drei.

  • printf ist eine sehr einfache und nützliche Methode, um Code zu debuggen. Wir könnten den Code dazu wie folgt abändern:

    #include <stdio.h>
    
    int main(void)
    {
        for (int i = 0; i <= 3; i++)
        {
            printf("i is %i\n", i);
            printf("#\n");
        }
    }
  • Wenn Sie diesen Code ausführen, werden Sie zahlreiche Ausgaben sehen, darunter i is 0, i is 1, i is 2 und i is 3. Wenn Sie das sehen, werden Sie vielleicht erkennen, dass der Code wie folgt korrigiert werden muss:

    #include <stdio.h>
    
    int main(void)
    {
        for (int i = 0; i < 3; i++)
        {
            printf("#\n");
        }
    }

    Beachten Sie, dass das “<=” durch “<” ersetzt wurde.

  • Dieser Code kann wie folgt weiter verbessert werden:

    #include <cs50.h>
    #include <stdio.h>
    
    void print_column(int height);
    
    int main(void)
    {
        int h = get_int("Height: ");
        print_column(h);
    }
    
    void print_column(int height)
    {
        for (int i = 0; i <= height; i++)
        {
            printf("#\n");
        }
    }

    Beachten Sie, dass das Kompilieren und Ausführen dieses Codes immer noch zu einem Fehler führt.

  • Um diesen Fehler zu beheben, werden wir ein neues Werkzeug einsetzen, das uns zur Verfügung steht.

  • Dieses Werkzeug zur Fehlersuche ist der sogenannte Debugger, ein Softwaretool, das verwendet wird, um Fehler im Code aufzuspüren.

  • In VS Code wird Ihnen ein vorkonfigurierter Debugger zur Verfügung gestellt.

  • Um diesen Debugger zu verwenden, setzen Sie als erstes einen Breakpoint (Haltepunkt), indem Sie links neben eine Zeile Ihres Codes klicken – direkt links neben die Zeilennummer. Wenn Sie dort klicken, wird ein roter Punkt erscheinen. Stellen Sie sich dies als ein Stoppschild vor, das den Rechner auffordert, eine Pause einzulegen, damit Sie sich ansehen können, was in diesem Teil Ihres Codes passiert.

    Haltepunkt
    Haltepunkt

  • Zweitens: Führen Sie debug50 ./buggy0 aus. Sie werden feststellen, dass, nachdem der Debugger aktiviert worden ist, eine Zeile Ihres Codes farbig markiert wird. Der Debugger hat den Rechner an dieser Codezeile angehalten. Beachten Sie in der oberen linken Ecke des Fensters, dass alle lokalen Variablen angezeigt werden, einschließlich h, das aktuell noch keinen Wert hat. Am oberen Rand des Fensters können Sie auf die mit einem Pfeil dargestellte Schaltfläche step over klicken (achten Sie auf den Tooltip beim Überfahren mit der Maus), und der Code wird weiter durchlaufen. Beachten Sie, wie sichder Wert von “h” ändert.

  • Dieses Werkzeug zeigt Ihnen zwar nicht, wo Ihr Fehler liegt, aber es hilft Ihnen, zu sehen, wie Ihr Code Schritt für Schritt abläuft. Sie können die Schaltfläche step into benutzen, um bei der Schritt-für-Schritt-Ausführung in eine Funktionen hineinzuschauen, anstatt diese mit step over zu überspringen.

  • Eine letzte Form der Fehlersuche wird Rubber Duck Debugging genannt. Wenn Sie Probleme mit Ihrem Code haben, überlegen Sie sich, wie Sie mit einer Gummiente laut über das Codeproblem sprechen können. Wenn Sie lieber nicht mit einer kleinen Plastikente sprechen möchten, können Sie gerne mit einem Menschen in Ihrer Nähe sprechen! Diese Person muss nicht wissen, wie man programmiert: Es ist einfach so, dass man oft merkt, wo das Problem ist, wenn man seine Gedanken in Worte fasst. Probieren Sie es aus, Rubber Duck Debugging funktioniert wirklich!

Arrays

  • In Woche 0 sprachen wir über Datentypen wie bool, int, char, string, usw.

  • Jeder Datentyp benötigt eine bestimmte Menge an Systemressourcen:

    • bool 1 Byte
    • int 4 Byte
    • long 8 Byte
    • Float 4 Bytes
    • Double 8 Bytes
    • char 1 Byte
    • string – ? Bytes, hängt von der Länge ab
  • In Ihrem Computer haben Sie nur eine begrenzte Menge an Speicherplatz zur Verfügung.

    Speicher
    Speicher

  • Physikalisch gesehen können Sie sich vorstellen, wie bestimmte Datentypen im Speicher Ihres Computers gespeichert werden. Sie können sich vorstellen, dass ein “Zeichen”, das nur 1 Byte Speicherplatz benötigt, wie folgt aussehen kann:

    1 Byte
    1 Byte

  • In ähnlicher Weise könnte ein int, der 4 Bytes benötigt, wie folgt aussehen:

    4 Bytes
    4 Bytes

  • Wir können ein Programm erstellen, mit dem wir dieses Konzept erforschen können. Geben Sie in Ihrem Terminal code scores.c ein und schreiben Sie den folgenden Code:

    #include <stdio.h>
    
    int main(void)
    {
        // Scores
        int score1 = 72;
        int score2 = 73;
        int score3 = 33;
    
        // Print average
        printf("Average: %f\n", (score1 + score2 + score3) / 3.0);
    }

    Beachten Sie, dass die Zahl auf der rechten Seite ein Fließkommawert von “3,0” ist, so dass die Berechnung am Ende als Fließkommawert dargestellt wird.

  • Wenn Sie make scores ausführen, läuft das Programm.

  • Sie können sich vorstellen, wie diese Variablen im Speicher abgelegt werden:

    Spielstände im Speicher
    Spielstände im Speicher

  • Arrays sind eine Möglichkeit, Daten im Speicher hintereinander zu speichern, so dass diese Daten leicht zugänglich sind.

  • Mit int scores[3] teilen Sie dem Compiler mit, dass er Ihnen drei direkt hintereinander liegende Speicherplätze zur Verfügung stellen soll, die so groß sind, dass jeweils ein int hineinpasst, also um drei scores zu speichern. Unser Programm können Sie dazu wie folgt ändern:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Get scores
        int scores[3];
        scores[0] = get_int("Score: ");
        scores[1] = get_int("Score: ");
        scores[2] = get_int("Score: ");
    
        // Print average
        printf("Average: %f\n", (scores[0] + scores[1] + scores[2]) / 3.0);
    }

    Beachten Sie, dass score[0] den Wert an dieser Stelle des Speichers anspricht, indem es im Array namens scores die Stelle 0 indiziert, den Wert, der an dieser Stelle gespeichert ist, auszulesen.

  • Sie sehen, dass der obige Code grundsätzlich korrekt funktioniert. Das Design ist aber schlecht – wir müssen uns beim Programmieren wiederholen. Überarbeiten Sie Ihren Code wie folgt:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Get scores
        int scores[3];
        for (int i = 0; i < 3; i++)
        {
            scores[i] = get_int("Score: ");
        }
    
        // Print average
        printf("Average: %f\n", (scores[0] + scores[1] + scores[2]) / 3.0);
    }

    Beachten Sie, wie wir in scores einen bestimmten Wert mit scores[i] indexieren, wobei i von der for-Schleife geliefert wird.

  • Wir können die Berechnung des Durchschnitts noch weiter vereinfachen, besser gesagt abstrahieren wir davon, wie die Implementierung genau funktioniert, indem wir sie in eine Funktion auslagern. Ändern Sie Ihren Code wie folgt:

    #include <cs50.h>
    #include <stdio.h>
    
    // Constant
    const int N = 3;
    
    // Prototype
    float average(int length, int array[]);
    
    int main(void)
    {
        // Get scores
        int scores[N];
        for (int i = 0; i < N; i++)
        {
            scores[i] = get_int("Score: ");
        }
    
        // Print average
        printf("Average: %f\n", average(N, scores));
    }
    
    float average(int length, int array[])
    {
        // Calculate average
        int sum = 0;
        for (int i = 0; i < length; i++)
        {
            sum += array[i];
        }
        return sum / (float) length;
    }

    Beachten Sie, dass hier eine neue Funktion namens “average” deklariert wird. Beachten Sie auch, dass ein konstanter Wert von “N” deklariert wird. Am wichtigsten ist, dass die Funktion average den Parameter int array[] übergeben bekommt, was bedeutet, dass der Compiler ein Array an diese Funktion übergibt.

  • Sie sehen: Arrays können sind nicht nur Container: Sie können auch zwischen Funktionen übergeben werden.

Strings

  • Ein „String“ ist einfach eine Reihe von Variablen des Typs char: also eine Folge von Zeichen, die direkt hintereinander im Speicher stehen.

  • Anhand des folgenden Bildes können Sie sehen, dass eine Zeichenkette eine Reihe von Zeichen ist, die mit dem ersten Zeichen beginnt und mit einem speziellen Zeichen, dem NUL-Zeichen" endet.

    Hallo mit Terminator
    Hallo mit Terminator

  • So ein Zeichen können Sie ein Strings mit dem Escapestring \0 erzeugen. Dieses Zeichen besteht aus 8 Bits, die alle den Wert “0” haben. In dezimaler Form würde Ihr char-Array also wie folgt aussehen:

    hi mit Dezimalzahl
    hi mit Dezimalzahl

  • Um dies in Ihrem eigenen Code zu implementieren, geben Sie code hi.c in das Terminalfenster ein und schreiben Sie folgenden Code:

    #include <stdio.h>
    
    int main(void)
    {
        char c1 = 'H';
        char c2 = 'I';
        char c3 = '!';
    
        printf("%c%c%c\n", c1, c2, c3);
    }

    Beachten Sie, dass dies die drei Zeichen wie eine Zeichenkette hintereinander.

  • Nehmen Sie die folgende Änderung an Ihrem Code vor:

    #include <stdio.h>
    
    int main(void)
    {
        char c1 = 'H';
        char c2 = 'I';
        char c3 = '!';
    
        printf("%i %i %i\n", c1, c2, c3);
    }

    Beachten Sie, dass nun die Integer-Werte ausgegeben werden, weil wir %c durch %i ersetzt haben

  • Um besser zu verstehen, wie eine “Zeichenkette” funktioniert, überarbeiten Sie Ihren Code wie folgt:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        string s = "HI!";
        printf("%c%c%c\n", s[0], s[1], s[2]);
    }

    Beachten Sie, dass die Anweisung printf drei Werte aus unserem Array namens s ausgibt.

  • Wie zuvor können wir %c durch %i ersetzen:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        string s = "HI!";
        printf("%i %i %i %i\n", s[0], s[1], s[2], s[3]);
    }

    Beachten Sie, dass dabei die ASCII-Codes der Zeichenkette einschließlich NUL ausgegeben werden – das vierte Zeichen (NUL), das das Ende des Strings markiert, hat man vorhin nicht gesehen.

  • Nehmen wir an, wir wollen sowohl HI! als auch BYE! sagen. Ändern Sie Ihren Code wie folgt:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        string s = "HI!";
        string t = "BYE!";
    
        printf("%s\n", s);
        printf("%s\n", t);
    }

    Beachten Sie, dass in diesem Beispiel zwei Zeichenketten deklariert und verwendet werden.

  • Sie können sich das wie folgt vorstellen:

    hallo und tschüss
    hallo und tschüss

  • Wir können diesen Code weiter verbessern. Ändern Sie Ihren Code wie folgt:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        string words[2];
    
        words[0] = "HI!";
        words[1] = "BYE!";
    
        printf("%s\n", words[0]);
        printf("%s\n", words[1]);
    }

    Beachten Sie, dass beide Zeichenketten in einem einzigen Array vom Typ “String” gespeichert sind.

Stringlänge

  • Ein typisches Problem in C ist die Ermittlung der Länge eines Arrays. Wie könnte man dies in Code umsetzen? Geben Sie code length.c in das Terminalfenster ein und programmieren Sie wie folgt:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt for user's name
        string name = get_string("Name: ");
    
        // Count number of characters up until '\0' (aka NUL)
        int n = 0;
        while (name[n] != '\0')
        {
            n++;
        }
        printf("%i\n", n);
    }

    Beachten Sie, dass dieser Code eine Schleife durchläuft, bis das Zeichen NUL gefunden wird.

  • Dieser Code kann verbessert werden, indem von der Implementierung der eigentlichen Zählung wie folgt abstrahiert wird:

    #include <cs50.h>
    #include <stdio.h>
    
    int string_length(string s);
    
    int main(void)
    {
        // Prompt for user's name
        string name = get_string("Name: ");
        int length = string_length(name);
        printf("%i\n", length);
    }
    
    int string_length(string s)
    {
        // Count number of characters up until '\0' (aka NUL)
        int n = 0;
        while (s[n] != '\0')
        {
            n++;
        }
        return n;
    }
  • Da dies ein so häufiges Problem in der Programmierung ist, gibt es schon fertigen Code in der Bibliothek string.h, um die Länge einer Zeichenkette zu ermitteln. Sie können in Zukunft die Länge einer Zeichenkette also einfach damit ermitteln. Ändern Sie Ihren Code wie folgt ab:

    #include <cs50.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(void)
    {
        // Prompt for user's name
        string name = get_string("Name: ");
        int length = strlen(name);
        printf("%i\n", length);
    }

    Beachten Sie, dass dieser Code die Bibliothek string.h verwendet, die am Anfang der Datei deklariert ist. Außerdem verwendet er eine Funktion aus dieser Bibliothek namens strlen (string length), die die Länge der übergebenen Zeichenkette berechnet.

  • Schauen Sie sich diesen Code an. Warum ist er nicht besonders effizient?

    #include <cs50.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(void)
    {
        // Prompt for user's name
        string name = get_string("Name: ");
        for (int i = 0; i < strlen(name); i++)
        {
          printf("%c", s[i]);
        }
        printf("\n");
    }

    Beachten Sie, dass dieser Code strlen() bei jedem Schleifendurchlauf aufruft. Besser wäre es, die Länge in einer Variablen vor der Schleife abzulegen oder zusammen mit i im Initialisierungsteil der for-Schleife zu initialisieren: int i = 0, n = strlen(name). Der Compiler kann solche Wiederholungen erkennen und selbständig wegoptimieren.

  • ctype.h ist eine weitere Bibliothek, die sehr nützlich ist. Stellen Sie sich vor, Sie wollten ein Programm erstellen, das alle Kleinbuchstaben in Großbuchstaben umwandelt. Geben Sie im Terminal-Fenster code uppercase.c ein und schreiben Sie den Code wie folgt:

    #include <cs50.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(void)
    {
        string s = get_string("Before: ");
        printf("After:  ");
        for (int i = 0, n = strlen(s); i < n; i++)
        {
            if (s[i] >= 'a' && s[i] <= 'z')
            {
                printf("%c", s[i] - 32);
            }
            else
            {
                printf("%c", s[i]);
            }
        }
        printf("\n");
    }

    Beachten Sie, dass dieser Code über jedes Zeichen in der Zeichenkette iteriert. Das Programm sieht sich also jedes Zeichen einzeln an. Wenn das Zeichen klein geschrieben ist, wird der Wert 32 abgezogen, um es in Großbuchstaben umzuwandeln.

  • Erinnern Sie sich an die vorherigen Vorlesungen, in denen wir uns dieses ASCII-Werte-Diagramm angesehen haben.

    ascii
    ascii

  • Wie man sieht gilt tatsächlich: Wenn man von einem Kleinbuchstaben 32 abzieht, erhält man eine Großbuchstabenversion desselben Zeichens.

  • Obwohl das Programm das tut, was wir wollen, gibt es einen einfacheren Weg, indem man die ctype.h-Bibliothek benutzt. Ändern Sie Ihr Programm dazu wie folgt:

    #include <cs50.h>
    #include <ctype.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(void)
    {
        string s = get_string("Before: ");
        printf("After:  ");
        for (int i = 0, n = strlen(s); i < n; i++)
        {
            if (islower(s[i]))
            {
                printf("%c", toupper(s[i]));
            }
            else
            {
                printf("%c", s[i]);
            }
        }
        printf("\n");
    }

    Beachten Sie, dass das Programm jedes Zeichen der Zeichenkette durchläuft. Der Funktion toupper wird s[i] übergeben. Jedes Zeichen (falls es ein Kleinbuchstabe ist) wird in Großbuchstaben umgewandelt.

  • toupper weiß, dass nur Kleinbuchstaben großgeschrieben werden sollen. Daher kann Ihr Code wie folgt vereinfacht werden:

    #include <cs50.h>
    #include <ctype.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(void)
    {
        string s = get_string("Before: ");
        printf("After: ");
        for (int i = 0, n = strlen(s); i < n; i++)
        {
            printf("%c", toupper(s[i]));
        }
        printf("\n");
    }

    Beachten Sie, dass dieser Code eine Zeichenkette mit Hilfe der ctype-Bibliothek in Großbuchstaben ausgibt.

  • Informieren Sie sich über die weiteren Funktionen der ctype-Bibliothek auf den Manual Pages.

Kommandozeilenparameter

  • Befehlszeilenargumente oder Kommandozeilenparameter sind die Argumente, die an Ihr Programm in der Kommandozeile übergeben werden. Zum Beispiel sind alle Zeichen, die Sie hinter dem Befehl clang eingegeben haben, die Befehlszeilenargumente von clang. Sie können diese Argumente auch in Ihren eigenen Programmen verwenden!

  • Geben Sie in Ihrem Terminalfenster code greet.c ein und schreiben Sie den folgenden Code:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        string answer = get_string("What's your name? ");
        printf("hello, %s\n", answer);
    }

    Beachten Sie, dass dies dem Benutzer “Hallo” sagt.

  • Aber wäre es nicht schön, wenn man Werte übergeben könnte, bevor das Programm überhaupt losläuft? Ändern Sie dazu Ihren Code wie folgt:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(int argc, string argv[])
    {
        if (argc == 2)
        {
            printf("hello, %s\n", argv[1]);
        }
        else
        {
            printf("hello, world\n");
        }
    }

    Beachten Sie, dass der main-Funktion sowohl argc, die Anzahl der Befehlszeilenargumente, als auch argv übergeben werden. Zweiteres ist ein Array aus allen Strings, die als Argumente in der Befehlszeile übergeben werden. Es gibt einen Mechanismus im Betriebssystem, der die auf der Kommandozeile eingegebenen Zeichen in diese Funktionsparameter überträgt, sodass Sie dort darauf Zugriff haben.

  • Bei diesem Programm würde die Ausführung von ./greet David dazu führen, dass das Programm hello, David sagt.

Exit-Code

  • Wenn ein Programm endet, kann es dem Rechner bzw. dem Betriebssystem einen speziellen Exit-Code (auch: Exit-Status oder Status-Code genannt) mitteilen.

  • Wenn ein Programm ohne Fehler beendet wird, wird üblicherweise der Exit-Code “0” zurückgegeben. Wenn ein Fehler auftritt, der zur Beendigung des Programms führt, wird oft der Wert “1” übergeben.

  • Welchen Exit-Code ein Programm zurückgibt, können Sie auf der Kommandozeile herausfinden, indem Sie echo $? eingeben nachdem das Programm beendet wurde. Achtung: Es wird immer der Exit-Code ausgegeben, den das direkt davor beendete Programm gesetzt hat.

  • Sie können dies ausprobieren, indem Sie “code status.c” eingeben und folgendes Programm programmieren:

    #include <cs50.h>
    #include <stdio.h>
    
    int main(int argc, string argv[])
    {
        if (argc != 2)
        {
            printf("Missing command-line argument\n");
            return 1;
        }
        printf("hello, %s\n", argv[1]);
        return 0;
    }

    Beachten Sie, dass Sie einen Exit-Status von “1” erhalten, wenn Sie das Programm ohne Parameter aufrufen. Wenn Sie aber bspw. ./status David angeben, erhalten Sie einen Exit-Status von 0.

  • Sie können sich vorstellen, wie Sie Teile des obigen Programms verwenden könnten, um zu prüfen, ob ein Benutzer die richtige Anzahl von Befehlszeilenargumenten angegeben hat.

Kryptographie

  • Kryptographie ist die Kunst, eine Nachricht zu verschlüsseln und zu entschlüsseln.

  • Klartext (Plaintext) und ein Schlüssel werden einer “Cipher”, also einem Chiffrier-Algorithmus zur Verfügung gestellt, wodurch ein verschlüsselter Text (Ciphertext) entsteht.

    Kryptographie
    Kryptographie

  • Der Schlüssel ist ein spezielles Argument, das der Chiffre zusammen mit dem Klartext übergeben wird. Die Chiffre verwendet den Schlüssel, um zu entscheiden, wie ihr Chiffrieralgorithmus implementiert werden soll.

Zusammenfassung

In dieser Lektion haben Sie weitere Einzelheiten über das Kompilieren und die Speicherung von Daten in einem Computer gelernt. Insbesondere haben Sie gelernt…

  • Wie ein Compiler grundsätlich funktioniert.
  • Wie man seinen Code Methoden debuggt.
  • Wie Sie Arrays in Ihrem Code verwenden.
  • Wie Arrays Daten in hintereinander liegenden Teilen des Speichers speichern.
  • Wie Zeichenketten einfach Arrays von Zeichen sind.
  • Wie Sie mit Arrays in Ihrem Code interagieren.
  • Wie Befehlszeilenargumente an Ihre Programme übergeben werden können.
  • Die Grundbausteine der Kryptographie.

Dies war Inf-Einf-B. Bis zum nächsten Mal!