Aller au contenu

Le chiffre de César en C et C++

Un programme complet à compiler tout de suite. Copiez-le, ou saisissez votre texte pour voir ce que le code fait de chaque lettre.

caesar.c
#include <stdio.h> void caesar(const char *text, int shift, char *result) {    int i;    for (i = 0; text[i] != '\0'; i++) {        char c = text[i];        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {            char base = (c <= 'Z') ? 'A' : 'a';            int index = ((c - base + shift) % 26 + 26) % 26;            result[i] = base + index;        } else {            result[i] = c;        }    }    result[i] = '\0';} int main(void) {    char result[100];    caesar("Bonjour à tous !", 3, result);    printf("%s\n", result);    // Erqmrxu à wrxv !    return 0;}
publicité

Compiler et exécuter

Enregistrez le programme sous le nom caesar.c et compilez-le avec gcc ou clang. Sous Windows, la seconde commande est caesar.exe.

Terminal
gcc caesar.c -o caesar./caesar

Le programme affiche le texte chiffré. Si le chiffre lui-même est nouveau pour vous, le guide l’explique sans code.

Comment fonctionne le code

En C, un char est un petit nombre, et l’on peut donc calculer avec lui : 'B' - 'A' vaut 1, le rang de la lettre dans l’alphabet. Une fois le décalage ajouté, le modulo maintient le résultat entre 0 et 25 : avec un décalage de 3, le Z tombe sur le C. base + index repasse ensuite du nombre à la lettre.

Une fonction C ne peut pas renvoyer une chaîne comme en Python ou en Java. L’appelant lui passe donc result, un tampon où écrire. Il doit contenir le texte et un octet de plus, car la dernière ligne de la fonction ajoute le '\0' qui termine toute chaîne en C.

Les espaces, la ponctuation et les chiffres passent par la branche else et sont recopiés tels quels. Même chose pour les lettres accentuées : en UTF-8, é occupe deux octets hors des plages de A à Z, et tous deux sont recopiés.

Déchiffrer en C

Inutile d’écrire une seconde fonction. Déchiffrer revient à décaler dans l’autre sens : dans main, il suffit de passer un nombre négatif.

C
caesar("Erqmrxu à wrxv !", -3, result);printf("%s\n", result);// Bonjour à tous !

D’où cette formule étrange. En C, % conserve le signe du nombre de gauche : -3 % 26 vaut -3, et ce rang n’existe pas. En ajoutant 26 puis en reprenant le modulo, on obtient 23, et le A revient au X. Dans la version Python, l’opérateur % s’en charge déjà.

Lire du texte au clavier

Si l’exercice demande une saisie au clavier, remplacez main par cette version. fgets lit toute la ligne, espaces compris, et n’écrit jamais plus que la taille indiquée, ce que scanf("%s") ne garantit pas.

C
#include <string.h> int main(void) {    char text[100], result[100];    int shift;     printf("Texte : ");    fgets(text, sizeof text, stdin);    text[strcspn(text, "\n")] = '\0';     printf("Décalage : ");    scanf("%d", &shift);     caesar(text, shift, result);    printf("%s\n", result);    return 0;}

fgets garde la touche Entrée dans la chaîne. La ligne avec strcspn la retire, et elle fonctionne aussi sur une ligne vide.

La clé en ligne de commande

Parfois, le programme doit recevoir ses données en arguments. Ici, le décalage et le texte viennent de argv :

C
#include <stdlib.h>#include <string.h> int main(int argc, char *argv[]) {    if (argc != 3) {        printf("Usage : %s <décalage> <texte>\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 "Bonjour à tous !"// Erqmrxu à wrxv !

argv[1] reste du texte, et atoi le transforme donc en nombre. La taille du tampon dépend de l’entrée : un long argument ne peut pas le faire déborder.

Casser le code par force brute

Avec 26 lettres, il n’y a que 25 décalages à essayer, et une courte boucle suffit pour lire un message sans la clé. Cherchez dans la sortie la ligne qui a du sens :

C
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("Erqmrxu à wrxv !");    // 1 Dqplqwt à vqwu !    // 2 Cpokpvs à upvt !    // 3 Bonjour à tous !    // ...    return 0;}

L’article sur l’attaque par force brute va plus loin, et le casseur en ligne fait la même chose dans votre navigateur.

Chiffres et lettres accentuées

Pour décaler plus que A à Z, gardez les caractères dans une chaîne et cherchez chacun avec strchr. Cette version décale aussi les chiffres. Le unsigned char compte, car passer un char négatif à tolower ou isupper est un comportement indéfini.

C
#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("Salle 101", 3, result);    printf("%s\n", result);    // Vdooh 434    return 0;}

Cela ne vaut que pour les caractères d’un seul octet. Les lettres accentuées en occupent deux en UTF-8 : il faudrait wchar_t et un réglage de locale qui varie selon les systèmes. Si vous en avez besoin, la version Python est bien plus simple, et notre outil de chiffre de César propose des alphabets tout prêts.

Chiffrer un fichier texte

Lisez le fichier ligne par ligne et écrivez chaque résultat dans un nouveau fichier. Vérifiez toujours fopen, qui renvoie NULL quand un fichier ne peut pas être ouvert.

C
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;}

Tester votre code

Comparez les chaînes avec strcmp, car == ne compare que leurs adresses. Un assert qui échoue arrête le programme et indique la ligne. Si tout va bien, il affiche OK.

C
#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;}

Le même chiffre en C++

En C++, std::string gère sa propre mémoire : la fonction peut simplement renvoyer le résultat, sans tampon à dimensionner. Le calcul ne change pas, pas plus que l’étape supplémentaire pour les négatifs, car % se comporte en C++ comme en C.

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("Bonjour à tous !", 3) << "\n";    // Erqmrxu à wrxv !}

Erreurs fréquentes

Le grand classique est l’oubli du '\0' : printf continue alors à lire au-delà du texte. Presque aussi courant, un tampon trop court d’un octet. Retirer le saut de ligne avec text[strlen(text) - 1] pose aussi problème, puisque sur une chaîne vide cela lit avant son début. Et sans le + 26 supplémentaire, le déchiffrement produit des symboles à la place des lettres.

Questions fréquentes

Quel compilateur et quelle version de C faut-il ?

N’importe quel compilateur compatible C99 convient, comme gcc ou clang. Nous avons compilé le code de cette page avec clang en mode C99, et la version C++ en C++17, sans avertissement.

Puis-je protéger de vraies données avec ce code ?

Non. La boucle ci-dessus le casse en quelques secondes. Gardez-le pour apprendre et pour les énigmes, et protégez les vraies données avec une librairie maintenue comme libsodium.

Comment vérifier mon résultat ?

Saisissez le même texte et le même décalage dans notre outil de chiffre de César et comparez. Vous pouvez aussi appeler l’API REST gratuite depuis vos tests.

Pour aller plus loin