C
La letra "x" se perdió en un archivo. Se escribió un programa para encontrarlo:
#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");
}
}
Fue compilado y corrió y finalmente gritó:
Hurray, letter lost in the file is finally found!
Durante muchos años, las cartas han sido rescatadas de esta manera hasta que llegó el nuevo tipo y optimizó el código. Estaba familiarizado con los tipos de datos y sabía que es mejor usar valores sin signo que con signo para valores no negativos, ya que tiene un rango más amplio y brinda cierta protección contra desbordamientos. Entonces cambió int por unsigned int . También conocía ascii lo suficientemente bien como para saber que siempre tienen un valor no negativo. Entonces él también cambió el carácter a un carácter sin signo . Compiló el código y se fue a casa orgulloso del buen trabajo que hizo. El programa se veía así:
#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");
}
}
Volvió a los estragos al día siguiente. Faltaba la letra "a" y, aunque se suponía que estaba en el "archivo_desierto" que contenía "abc", el programa la estaba buscando para siempre imprimiendo solo:
Searching file for missing letter a...
Despidieron al tipo y volvieron a la versión anterior recordando que nunca se deben optimizar los tipos de datos en el código de trabajo.
¿Pero cuál es la lección que deberían haber aprendido aquí?
En primer lugar, si echa un vistazo a la tabla ASCII, notará que no hay EOF. Esto se debe a que EOF no es un carácter, sino un valor especial devuelto por fgetc (), que puede devolver un carácter extendido a int o -1 que denota el final del archivo.
Siempre y cuando estemos usando char con signo, todo funciona bien: fgetc () extiende también un char igual a 50 a int igual a 50. Luego lo transformamos nuevamente a char y todavía tenemos 50. Lo mismo sucede con -1 o cualquier otra salida que provenga de fgetc ().
Pero mira lo que sucede cuando usamos char sin firmar. Comenzamos con un char en fgetc (), lo extendemos a int y luego queremos tener un char sin firmar. El único problema es que no podemos preservar -1 en caracteres sin signo. El programa lo almacena como 255, que ya no es igual a EOF.
Consideración
Si echa un vistazo a la sección 3.1.2.5 Tipos en copia de la documentación de ANSI C , descubrirá que si char está firmado o no depende únicamente de la implementación. Entonces, el tipo probablemente no debería ser despedido ya que encontró un error muy complicado acechando en el código. Podría salir al cambiar el compilador o al cambiar a una arquitectura diferente. Me pregunto quién sería despedido si el error saliera en tal caso;)
PD. El programa fue construido alrededor del error mencionado en el lenguaje ensamblador de PC por Paul A. Carter