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.
- Écrire
copie_sure(char dest[], int taille_dest, const char src[])qui copie en tronquant si nécessaire, et garantit toujours une chaîne bien terminée. - Que doit-elle faire si
taille_destvaut ? - Écrire un jeu de tests couvrant les cas limites.
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.