Adloun

Probleme – Une chaîne sûre

Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 1 — Le langage C

Énoncé

Les fonctions strcpy et strcat ne vérifient pas la taille de leur destination.

Corrigé

1. La fonction.


/* Copie src dans dest, en tronquant si nécessaire. dest est TOUJOURS une
   chaîne valide au retour. Renvoie true si la copie est complète.
   Préconditions : dest pointe sur taille_dest cases, taille_dest >= 0,
                   src est une chaîne valide, dest et src ne se recouvrent pas. */
bool copie_sure(char dest[], int taille_dest, const char src[]) {
    assert(dest != NULL && src != NULL && taille_dest >= 0);
    if (taille_dest == 0) { return false; }    /* aucune place, pas même le '\0' */
    int i = 0;
    /* INVARIANT : dest[0..i-1] reproduit src[0..i-1], et i < taille_dest. */
    while (i < taille_dest - 1 && src[i] != '\0') {
        dest[i] = src[i];
        i = i + 1;
    }
    dest[i] = '\0';                            /* TOUJOURS terminée */
    return src[i] == '\0';                     /* complète ssi on a tout copié */
}

2. Le cas taille_dest == 0. Il faut le traiter à part, et c'est le point délicat : avec zéro case, on ne peut pas même écrire la sentinelle. Toute écriture serait un débordement. La fonction rend donc false sans rien écrire. Une version qui écrirait dest[0] = '\0' avant de tester serait fausse — précisément dans le cas où l'on cherche à se protéger.

3. Le jeu de tests, partitionné et éprouvant les limites :


void tester_copie_sure(void) {
    char d[8];

    /* cas nominal : ça tient */
    assert(copie_sure(d, 8, "abc") == true);
    assert(strcmp(d, "abc") == 0);

    /* limite : exactement la place, sentinelle comprise */
    assert(copie_sure(d, 8, "abcdefg") == true);
    assert(strcmp(d, "abcdefg") == 0);

    /* limite : un caractère de trop */
    assert(copie_sure(d, 8, "abcdefgh") == false);
    assert(strcmp(d, "abcdefg") == 0);      /* tronqué, mais VALIDE */

    /* cas dégénérés */
    assert(copie_sure(d, 8, "") == true);
    assert(strcmp(d, "") == 0);
    assert(copie_sure(d, 1, "abc") == false);
    assert(strcmp(d, "") == 0);             /* place pour la seule sentinelle */
    assert(copie_sure(d, 0, "abc") == false);

    printf("copie_sure : tous les tests passent\n");
}

Ce que ce jeu illustre : les cinq premiers cas sont aux frontières — exactement la place, un de trop, un de moins. Le chapitre chap:discipline l'énonce comme règle ; ici on voit pourquoi : les trois fautes plausibles de cette fonction ( écrit , sentinelle oubliée, cas zéro non traité) sont toutes révélées par ces cas-là, et par aucun cas « du milieu ».

Les autres exercices de ce chapitre Le cours du chapitre

Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.