C
La lettre "x" a été perdue dans un fichier. Un programme a été écrit pour le trouver:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
FILE* fp = fopen("desert_file", "r");
char letter;
char missing_letter = argv[1][0];
int found = 0;
printf("Searching file for missing letter %c...\n", missing_letter);
while( (letter = fgetc(fp)) != EOF ) {
if (letter == missing_letter) found = 1;
}
printf("Whole file searched.\n");
fclose(fp);
if (found) {
printf("Hurray, letter lost in the file is finally found!\n");
} else {
printf("Haven't found missing letter...\n");
}
}
Il a été compilé et à courir et il a finalement crié:
Hurray, letter lost in the file is finally found!
Pendant de nombreuses années, les lettres ont été sauvées de cette façon jusqu'à ce que le nouveau type optimise le code. Il connaissait bien les types de données et savait qu'il valait mieux utiliser des signatures non signées que signées pour les valeurs non négatives, car cette plage est plus étendue et offre une certaine protection contre les débordements. Alors il a changé int en unsigned int . Il connaissait également suffisamment l'ascii pour savoir qu'ils avaient toujours une valeur non négative. Donc, il a également changé de caractère en caractère non signé . Il compila le code et rentra chez lui fier du bon travail qu'il accomplissait. Le programme ressemblait à ceci:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
FILE* fp = fopen("desert_file", "r");
unsigned char letter;
unsigned char missing_letter = argv[1][0];
unsigned int found = 0;
printf("Searching file for missing letter %c...\n", missing_letter);
while( (letter = fgetc(fp)) != EOF ) {
if (letter == missing_letter) found = 1;
}
printf("Whole file searched.\n");
fclose(fp);
if (found) {
printf("Hurray, letter lost in the file is finally found!\n");
} else {
printf("Haven't found missing letter...\n");
}
}
Il est revenu à un désastre le lendemain. La lettre "a" était manquante et même si elle était supposée se trouver dans le "fichier_du_ désert" contenant "abc", le programme le cherchait pour toujours en imprimant:
Searching file for missing letter a...
Ils ont renvoyé le gars et sont revenus à la version précédente en se rappelant qu'il ne fallait jamais optimiser les types de données dans du code de travail.
Mais quelle est la leçon qu'ils auraient dû apprendre ici?
Tout d’abord, si vous regardez la table ascii, vous remarquerez qu’il n’ya pas de fichier EOF. En effet, EOF n'est pas un caractère mais une valeur spéciale renvoyée par fgetc (), qui peut renvoyer un caractère étendu en int ou -1 indiquant la fin du fichier.
Tant que nous utilisons un caractère signé, tout fonctionne correctement - un caractère égal à 50 est étendu par fgetc () dans un entier égal à 50 également. Ensuite, nous le transformons en caractère et avons toujours 50. Même chose pour -1 ou toute autre sortie provenant de fgetc ().
Mais regardez ce qui se passe lorsque nous utilisons des caractères non signés. Nous commençons par un caractère dans fgetc (), étendons-le à int puis souhaitons avoir un caractère non signé. Le seul problème est que nous ne pouvons pas conserver -1 dans un caractère non signé. Le programme la stocke sous la forme 255, qui n'est plus égale à EOF.
Caveat
Si vous consultez la section 3.1.2.5 Types de copie de la documentation ANSI C, vous verrez que le caractère signé ou non dépend de la mise en œuvre. Donc, le gars ne devrait probablement pas être licencié, car il a trouvé un bogue très délicat caché dans le code. Cela pourrait apparaître lors du changement de compilateur ou lors du passage à une architecture différente. Je me demande qui serait viré si le bogue venait à apparaître dans un tel cas;)
PS Le programme a été construit autour du bogue mentionné dans PC Assembly Language par Paul A. Carter