Diferencia entre int y char en getchar / fgetc y putchar / fputc?

getchar aprender C por mi cuenta y estoy un poco confundido con getchar y putchar :

1

 #include  int main(void) { char c; printf("Enter characters : "); while((c = getchar()) != EOF){ putchar(c); } return 0; } 

2

 #include  int main(void) { int c; printf("Enter characters : "); while((c = getchar()) != EOF){ putchar(c); } return 0; } 

La función de biblioteca C int putchar(int c) escribe un carácter (un carácter sin signo) especificado por el argumento char a stdout.

La función de biblioteca C int getchar(void) obtiene un carácter (un carácter sin signo) de stdin. Esto es equivalente a getc con stdin como argumento.

¿Significa que putchar() acepta tanto int como char o cualquiera de ellos y para getchar() deberíamos usar un int o char ?

TL; DR:

  • char c; c = getchar(); está mal, roto y con errores .
  • int c; c = getchar(); es correcto

Esto también se aplica a getc y fgetc , si no aún más, porque uno leería a menudo hasta el final del archivo.


getchar siempre el valor de retorno de getchar ( fgetc , getc …) (y putchar ) inicialmente en una variable de tipo int .

El argumento para putchar puede ser cualquiera de int , char , signed char o unsigned char ; su tipo no importa, y todos funcionan de la misma manera, aunque uno puede dar como resultado números enteros positivos y otros en números enteros negativos que se pasan para los caracteres anteriores e incluyen \200 (128).


La razón por la que debe usar int para almacenar el valor de retorno de getchar y putchar es que cuando se alcanza la condición de fin de archivo (o se produce un error de E / S), ambos devuelven el valor de la macro EOF que es una constante entera negativa, (generalmente -1 ) .

Para getchar , si el valor de retorno no es EOF , es el unsigned char lectura zero-extended a un int . Es decir, suponiendo caracteres de 8 bits, los valores devueltos pueden ser 0255 o el valor de la macro EOF ; asumiendo de nuevo el carácter de 8 bits, no hay forma de exprimir estos 257 valores distintos en 256 para que cada uno de ellos pueda identificarse de manera única.


Ahora, si lo almacena en char , el efecto dependerá de si el tipo de carácter está firmado o no de manera predeterminada . Esto varía de comstackdor a comstackdor, architecture a architecture. Si se firma EOF y suponiendo que EOF se define como -1 , entonces tanto EOF como el carácter '\377' en la entrada se compararían con EOF ; se extenderán a (int)-1 .

Por otro lado, si char no está firmado (como lo es de forma predeterminada en los procesadores ARM, incluidos los sistemas Raspberry PI , y parece ser cierto también para AIX ), no hay ningún valor que pueda almacenarse en c que compare igual a -1 ; incluido EOF ; en lugar de irrumpir en EOF , su código generaría un único \377 caracteres.

El peligro aquí es que con las char firmadas el código parece estar funcionando correctamente aunque todavía esté terriblemente roto: uno de los valores de entrada legales se interpreta como EOF . Además, C89, C99, C11 no exige un valor para EOF ; solo dice que EOF es una constante entera negativa; por lo tanto, en lugar de -1 , podría decirse -224 en una implementación particular, lo que haría que los espacios se comporten como EOF .

gcc tiene el -funsigned-char que se puede usar para hacer que el char no esté firmado en esas plataformas donde se registra por defecto:

 % cat test.c #include  int main(void) { char c; printf("Enter characters : "); while((c= getchar()) != EOF){ putchar(c); } return 0; } 

Ahora lo ejecutamos con char firmado:

 % gcc test.c && ./a.out Enter characters : sfdasadfdsaf sfdasadfdsaf ^D % 

Parece estar funcionando bien. Pero con char sin signo:

 % gcc test.c -funsigned-char && ./a.out Enter characters : Hello world Hello world                            ^C % 

Es decir, intenté presionar Ctrl-D varias veces, pero se imprimió un for por cada EOF lugar de romper el ciclo.

Ahora, de nuevo, para el caso char firmado, no se puede distinguir entre char 255 y EOF en Linux, rompiéndolo para datos binarios y cosas por el estilo:

 % gcc test.c && echo -e 'Hello world\0377And some more' | ./a.out Enter characters : Hello world % 

Solo la primera parte hasta el escape \0377 fue escrita en stdout.


Tenga en cuenta que las comparaciones entre las constantes de caracteres y un int contiene el valor del carácter sin firmar podrían no funcionar como se esperaba (por ejemplo, la constante de carácter 'ä' en ISO 8859-1 significaría el valor con signo -28 . Por lo tanto, suponiendo que escriba código que leería hasta que 'ä' en la página de códigos ISO 8859-1, harías

 int c; while((c = getchar()) != EOF){ if (c == (unsigned char)'ä') { /* ... */ } } 

Debido a la promoción entera, todos los valores char encajan en un int , y se promueven automáticamente en llamadas a función, por lo que puedes dar cualquiera de int , char , signed char o unsigned char a putchar como argumento (no para almacenar su valor de retorno), y funcionaría como se esperaba.

El valor real pasado en el entero puede ser positivo o incluso negativo; por ejemplo, la constante de caracteres \377 sería negativa en un sistema de 8 bitchar, donde char está firmado; sin embargo, putchar (o fputc realidad) arrojará el valor a un char sin signo. C11 7.21.7.3p2 :

2 La función fputc escribe el carácter especificado por c (convertido a un carácter sin signo) en la secuencia de salida a la que apunta el flujo […]

(énfasis mío)

Es decir, se garantiza que el fputc convertirá la c dada como si fuera (unsigned char)c

Siempre use int para guardar caracteres de getchar() ya que la constante EOF es de tipo int . Si usa char , la comparación con EOF no es correcta.

putchar() embargo, puede pasar de manera segura char a putchar() ya que se promocionará a int automáticamente.

Nota : Técnicamente, usar char funcionará en la mayoría de los casos, pero luego no se puede tener el carácter 0xFF, ya que se interpretará como EOF debido a la conversión de tipo. Para cubrir todos los casos, siempre use int . Como @Ilja lo puso – int es necesario para representar los 256 valores de caracteres posibles y el EOF , que son 257 valores posibles en total, que no se pueden almacenar en el tipo de caracteres.