Kompilieren und ausführen
Speichere das Programm als caesar.c und kompiliere es mit gcc oder clang. Unter Windows lautet der zweite Befehl caesar.exe.
gcc caesar.c -o caesar./caesarDas Programm gibt den verschlüsselten Text aus. Ist die Chiffre selbst noch neu für dich, hilft die Anleitung ganz ohne Code.
So funktioniert der Code
Ein char ist in C eine kleine Zahl, du kannst also damit rechnen: 'H' - 'A' ergibt 7, die Position im Alphabet. Nach dem Addieren der Verschiebung hält der Modulo das Ergebnis zwischen 0 und 25, deshalb landet Z bei der Verschiebung 3 auf C. base + index macht aus der Zahl wieder einen Buchstaben.
Eine C-Funktion kann keinen String zurückgeben wie Python oder Java. Deshalb übergibt der Aufrufer mit result einen Puffer, in den sie schreibt. Er muss den Text und ein weiteres Byte fassen, denn die letzte Zeile der Funktion hängt das '\0' an, mit dem jeder C-String endet.
Leerzeichen, Satzzeichen und Ziffern laufen durch den else-Zweig und werden unverändert kopiert. Das gilt auch für Umlaute: In UTF-8 ist ä zwei Bytes groß, beide liegen außerhalb von A bis Z und bleiben, wie sie sind.
Entschlüsseln in C
Eine zweite Funktion brauchst du nicht. Entschlüsseln ist dieselbe Verschiebung in die andere Richtung, also übergibst du in main eine negative Zahl:
caesar("Kdoor Zhow!", -3, result);printf("%s\n", result);// Hallo Welt!Daher kommt die seltsame Formel. In C behält % das Vorzeichen der linken Zahl, -3 % 26 ergibt also -3, und diese Position gibt es nicht. Mit 26 dazu und einem zweiten Modulo kommt 23 heraus, und A geht zurück zu X. In der Python-Version erledigt das schon der Operator %.
Text von der Tastatur lesen
Verlangt die Aufgabe eine Tastatureingabe, ersetze main durch diese Version. fgets liest eine ganze Zeile samt Leerzeichen und schreibt nie mehr als die angegebene Größe, was scanf("%s") nicht garantiert.
#include <string.h> int main(void) { char text[100], result[100]; int shift; printf("Text: "); fgets(text, sizeof text, stdin); text[strcspn(text, "\n")] = '\0'; printf("Verschiebung: "); scanf("%d", &shift); caesar(text, shift, result); printf("%s\n", result); return 0;}fgets behält die Enter-Taste im String. Die Zeile mit strcspn entfernt sie und funktioniert auch bei einer leeren Zeile.
Den Schlüssel als Kommandozeilenargument
Manchmal soll das Programm seine Daten als Argumente bekommen. Hier stammen Verschiebung und Text aus argv:
#include <stdlib.h>#include <string.h> int main(int argc, char *argv[]) { if (argc != 3) { printf("Aufruf: %s <Verschiebung> <Text>\n", argv[0]); return 1; } char result[strlen(argv[2]) + 1]; caesar(argv[2], atoi(argv[1]), result); printf("%s\n", result); return 0;} // ./caesar 3 "Hallo Welt!"// Kdoor Zhow!argv[1] ist noch Text, deshalb macht atoi daraus eine Zahl. Die Puffergröße richtet sich nach der Eingabe, ein langes Argument kann ihn also nicht überlaufen.
Die Chiffre mit Brute Force knacken
Bei 26 Buchstaben gibt es nur 25 Verschiebungen zum Ausprobieren, also liest eine kurze Schleife die Nachricht auch ohne Schlüssel. Du musst nur die Zeile finden, die einen Sinn ergibt:
void crack(const char *text) { char result[100]; for (int shift = 1; shift < 26; shift++) { caesar(text, -shift, result); printf("%d %s\n", shift, result); }} int main(void) { crack("Kdoor Zhow!"); // 1 Jcnnq Ygnv! // 2 Ibmmp Xfmu! // 3 Hallo Welt! // ... return 0;}Mehr dazu steht im Artikel über den Brute-Force-Angriff, und der Caesar-Knacker erledigt dasselbe im Browser.
Ziffern und Umlaute
Soll mehr als A bis Z verschoben werden, legst du die Zeichen in einem String ab und suchst jedes mit strchr. Diese Version verschiebt auch Ziffern. Das unsigned char ist wichtig, denn ein negativer char an tolower oder isupper führt zu undefiniertem Verhalten.
#include <ctype.h>#include <string.h> void caesar_alphabet(const char *text, int shift, char *result) { const char *alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"; int size = strlen(alphabet); int i; for (i = 0; text[i] != '\0'; i++) { unsigned char c = text[i]; const char *found = strchr(alphabet, tolower(c)); if (found == NULL) { result[i] = c; } else { int index = ((found - alphabet + shift) % size + size) % size; result[i] = isupper(c) ? toupper(alphabet[index]) : alphabet[index]; } } result[i] = '\0';} int main(void) { char result[100]; caesar_alphabet("Raum 101", 3, result); printf("%s\n", result); // Udxp 434 return 0;}Das klappt nur mit Zeichen, die in ein Byte passen. Umlaute und ß belegen in UTF-8 zwei Bytes, du bräuchtest also wchar_t und Locale-Einstellungen, die sich zwischen Systemen unterscheiden. Viel einfacher geht das in der Python-Version, und unser Caesar-Tool kennt das deutsche Alphabet bereits.
Eine Textdatei verschlüsseln
Lies die Datei Zeile für Zeile und schreib jedes Ergebnis in eine neue Datei. Prüfe immer das Ergebnis von fopen, denn es liefert NULL, wenn sich eine Datei nicht öffnen lässt.
int main(void) { char line[1000], result[1000]; FILE *in = fopen("message.txt", "r"); FILE *out = fopen("encrypted.txt", "w"); if (in == NULL || out == NULL) { perror("fopen"); return 1; } while (fgets(line, sizeof line, in) != NULL) { caesar(line, 3, result); fputs(result, out); } fclose(in); fclose(out); return 0;}Den Code testen
Vergleiche Strings mit strcmp, denn == vergleicht nur ihre Adressen. Ein fehlgeschlagenes assert stoppt das Programm und nennt die Zeile. Ist alles in Ordnung, gibt es OK aus.
#include <assert.h>#include <string.h> int main(void) { char r[100]; caesar("ABC", 3, r); assert(strcmp(r, "DEF") == 0); caesar("xyz", 3, r); assert(strcmp(r, "abc") == 0); caesar("Hello, World!", 5, r); assert(strcmp(r, "Mjqqt, Btwqi!") == 0); caesar("A", -27, r); assert(strcmp(r, "Z") == 0); caesar("é", 3, r); assert(strcmp(r, "é") == 0); printf("OK\n"); return 0;}Dieselbe Chiffre in C++
In C++ verwaltet std::string seinen Speicher selbst. Die Funktion kann das Ergebnis also einfach zurückgeben, und du musst keinen Puffer berechnen. Die Rechnung bleibt gleich, auch der Zusatzschritt für negative Zahlen, denn % verhält sich in C++ wie in C.
#include <iostream>#include <string> std::string caesar(std::string text, int shift) { for (char &c : text) { if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { char base = (c <= 'Z') ? 'A' : 'a'; c = base + ((c - base + shift) % 26 + 26) % 26; } } return text;} int main() { std::cout << caesar("Hallo Welt!", 3) << "\n"; // Kdoor Zhow!}Häufige Fehler
Der Klassiker ist das fehlende '\0', sodass printf über das Textende hinaus weiterliest. Fast genauso oft ist der Puffer ein Byte zu kurz. Auch das Entfernen des Zeilenumbruchs mit text[strlen(text) - 1] geht schief, denn bei einem leeren String liest es vor dessen Anfang. Und ohne das zusätzliche + 26 liefert das Entschlüsseln Symbole statt Buchstaben.
Häufige Fragen
Welchen Compiler und welchen C-Standard brauche ich?
Jeder Compiler mit C99-Unterstützung reicht aus, etwa gcc oder clang. Wir haben den Code dieser Seite mit clang im C99-Modus kompiliert und die C++-Version als C++17, ohne Warnungen.
Kann ich damit echte Daten schützen?
Nein. Die Schleife von oben knackt die Chiffre in Sekunden. Nimm sie zum Lernen und für Rätsel. Für echte Daten brauchst du eine gepflegte Bibliothek wie libsodium.
Wie prüfe ich, ob mein Ergebnis stimmt?
Gib denselben Text und dieselbe Verschiebung in unser Caesar-Tool ein und vergleiche. Aus deinen Tests heraus kannst du auch die kostenlose REST-API aufrufen.