С
Буква "х" была потеряна в файле. Программа была написана, чтобы найти это:
#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");
}
}
Он был скомпилирован и запущен, и наконец он кричал:
Hurray, letter lost in the file is finally found!
В течение многих лет письма спасались таким образом, пока не пришел новый парень и не оптимизировал код. Он был знаком с типами данных и знал, что лучше использовать без знака, чем со знаком, для неотрицательных значений, поскольку он имеет более широкий диапазон и обеспечивает некоторую защиту от переполнения. Таким образом, он изменил int на unsigned int . Он также знал ASCII достаточно хорошо, чтобы знать, что они всегда имеют неотрицательную ценность. Таким образом, он также изменил char на unsigned char . Он скомпилировал код и пошел домой, гордясь хорошей работой, которую он сделал. Программа выглядела так:
#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");
}
}
Он вернулся в хаос на следующий день. Буква «а» отсутствовала, и, хотя она должна была находиться в файле «desert_file», содержащем «abc», программа искала его навсегда, распечатывая только:
Searching file for missing letter a...
Они уволили парня и откатились к предыдущей версии, вспомнив, что нельзя оптимизировать типы данных в рабочем коде.
Но какой урок они должны были извлечь здесь?
Прежде всего, если вы посмотрите на таблицу ascii, вы заметите, что EOF отсутствует. Это потому, что EOF - это не символ, а специальное значение, возвращаемое функцией fgetc (), которая может возвращать символ, расширенный до int, или -1, обозначающий конец файла.
Пока мы используем подписанный char, все работает хорошо - char, равный 50, также расширяется с помощью fgetc () до int, равного 50. Затем мы преобразуем его обратно в char и оставляем 50. То же самое происходит для -1 или любого другого вывода, получаемого из fgetc ().
Но посмотрите, что происходит, когда мы используем unsigned char. Мы начинаем с символа в fgetc (), расширяем его до int, а затем хотим иметь неподписанный символ. Единственная проблема в том, что мы не можем сохранить -1 в неподписанном символе. Программа хранит его как 255, который больше не равен EOF.
Предостережение
Если вы взглянете на раздел 3.1.2.5 Типы в копии документации ANSI C, вы обнаружите, что то, является ли char подписанным или нет, зависит исключительно от реализации. Так что парня, вероятно, не стоит увольнять, поскольку он обнаружил в коде очень хитрую ошибку. Это может произойти при смене компилятора или переходе на другую архитектуру. Интересно, кого бы уволили, если бы баг вышел в таком случае;)
PS. Программа была построена вокруг ошибки, упомянутой в языке сборки ПК Полом А. Картером