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

9 minutos de lectura

avatar de usuario
Raghib Hasan

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

1

#include <stdio.h>

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

2

#include <stdio.h>

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 en stdout.

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

Eso significa putchar() acepta ambos int y char o cualquiera de ellos y para getchar() ¿Deberíamos usar un int o char?

  • ¿Por qué la variable utilizada para contener el valor de retorno de getchar debe declararse como int?

    – phuclv

    15 mayo 2017 a las 12:15

  • Posible duplicado de ¿Por qué la variable utilizada para contener el valor de retorno de getchar debe declararse como int?

    – phuclv

    15 mayo 2017 a las 12:15

  • @LưuVĩnhPhúc o al revés. La edad de una pregunta no importa

    – Antti Haapala — Слава Україні

    19 mayo 2017 a las 10:05


avatar de usuario
Antti Haapala — Слава Україні

TL;RD:

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

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


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

Él 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 negativos para los caracteres superiores e incluidos \200 (128).


la razon por la que tu deber usar int para almacenar el valor devuelto de ambos 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 getcharsi el valor de retorno no es EOFes la lectura unsigned char cero extendido a un int. Es decir, asumiendo caracteres de 8 bits, los valores devueltos pueden ser 0255 o el valor de la macro EOF; suponiendo de nuevo un carácter de 8 bits, no hay forma de comprimir estos 257 valores distintos en 256 para que cada uno de ellos pueda identificarse de forma única.


Ahora, si lo almacenaste en char en cambio, el efecto dependería de si el tipo de carácter está firmado o no firmado por defecto. Esto varía de un compilador a otro, de una arquitectura a otra. Si char está firmado y asumiendo EOF Se define como -1entonces ambas cosas EOF y carácter '\377' en la entrada se compararía igual a EOF; se extenderían por signo a (int)-1.

Por otro lado, si char no está firmado (como lo es de forma predeterminada en los procesadores ARM, incluido Sistemas de frambuesa PI; y parece ser cierto para AIX también), hay no valor que podría ser almacenado en c que compararía igual a -1; incluido EOF; en lugar de estallar en EOFsu código generaría un solo \377 personaje.

El peligro aquí es que con firmado chares el codigo parece estar funcionando correctamente a pesar de que 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 eso EOF es una constante entera negativa; así en lugar de -1 bien podría decirse -224 en una implementación particular, lo que haría que los espacios se comportaran como EOF.

gcc tiene el interruptor -funsigned-char que se puede utilizar para hacer la char sin firmar en aquellas plataformas donde por defecto está firmado:

% cat test.c
#include <stdio.h>

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

Ahora lo ejecutamos con firmado char:

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

Parece estar funcionando bien. Pero sin firmar char:

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

Es decir, traté de presionar Ctrl-D allí muchas veces pero un se imprimió para cada EOF en lugar de romper el bucle.

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

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

Sólo la primera parte hasta la \0377 escape fue escrito en stdout.


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

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

Debido a la promoción de enteros, todos char los valores encajan en un inty se promocionan automáticamente en llamadas de función, por lo que puede dar cualquiera de int, char, signed char o unsigned char para putchar como un 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 carácter \377 sería negativo en un sistema de caracteres de 8 bits donde char está firmado; sin embargo putchar (o fputc en realidad) convertirá el valor en un carácter sin firmar. C11 7.21.7.3p2:

2 La función fputc escribe el carácter especificado por c (convertido a un char sin firmar) al flujo de salida apuntado por flujo […]

(énfasis mío)

es decir, el fputc estará garantizado para convertir el dado c como si por (unsigned char)c

  • Todavía no entiendo por qué el código está horriblemente roto con caracteres firmados. Después de todo, ¿no almacena bits? Al igual que ent? Espero entender cuál es este peligro. Después de todo, si almacena el valor binario 10 en una variable char o int, es exactamente el mismo número binario. Por lo tanto, mismo número entero.

    –Judismar Arpini Junior

    12 de febrero de 2016 a las 7:14

  • Especialmente si vives en Turquía, donde se usa la letra ÿ (y-diéresis, U+00FF, LETRA Y MINÚSCULA LATINA CON DIÉRESIS), luego escribes esa letra en el código que guarda el resultado de getchar() en un firmado char type se detectaría como EOF, al igual que si hubiera escrito Control-D (Unix) o Control-Z (Windows), que indican “no más datos” o EOF. Entonces, el problema es que un carácter legítimo (ÿ) se trata como EOF cuando no debería serlo. Es casi tan malo como nunca tratar nada como EOF.

    –Jonathan Leffler

    12 de febrero de 2016 a las 7:55

  • La norma especifica (por fgetc() pero getchar() se implementa en términos de getc(stdin) y getc() es equivalente a fgetc()) ese: Si el indicador de fin de archivo para el flujo de entrada señalado por stream no está configurado y hay un carácter siguiente presente, el fgetc función obtiene ese carácter como un unsigned char convertido en un int … Si se establece el indicador de fin de archivo para la secuencia, o si la secuencia se encuentra al final del archivo, se establece el indicador de fin de archivo para la secuencia y el fgetc función devuelve EOF.

    –Jonathan Leffler

    12 de febrero de 2016 a las 7:58


  • La preocupación por 'ä' es nuevo para mi Parece que C11 §6.4.4.4 10 es la cita relevante: “Si una constante de carácter entero contiene un solo carácter o secuencia de escape, su valor es el que resulta cuando un objeto con tipo char cuyo valor es el del carácter único o la secuencia de escape se convierte a tipo int.”

    – chux – Reincorporar a Monica

    14 de abril de 2017 a las 15:21

  • Tuve una discusión con alguien, sobre si necesitas o no usar int contra char con getchar(), en unos comentarios hace un tiempo. ¡La próxima vez los señalaré aquí!

    – ad absurdum

    14/06/2017 a las 21:35

avatar de usuario
JuanLM

Siempre usa int para guardar el personaje de getchar() como EOF constante es de int tipo. Si utiliza char entonces la comparación contra EOF no es correcto.

Puedes pasar con seguridad char para putchar() aunque como será promovido a int automáticamente.

Nota: técnicamente utilizando char funcionará en la mayoría de los casos, pero no 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 usar int. Como dijo @Ilja: int es necesario para representar los 256 valores de caracteres posibles y la EOFque son 257 valores posibles en total, que no se pueden almacenar en char tipo.

  • “Si usa char, entonces la comparación con EOF no es correcta”. No estoy seguro si lo consigo. Una variable char almacena un número entero, por lo que es lo mismo, en este caso, que usar int.

    –Judismar Arpini Junior

    12 de febrero de 2016 a las 7:00


  • Debería decir “use int si espera 0xFF en flujos o si no sabe si char esté firmado o no”, es decir, “siempre”.

    – Antti Haapala — Слава Україні

    12 de febrero de 2016 a las 7:29

  • @JudismarJunior “¿Entonces me estás diciendo que es mejor leer un valor de char y almacenarlo en una variable int?” ¡Sí! Bueno, al menos hasta que lo compares con EOF. Después de eso, puedes guardarlo como char. Pero también estoy diciendo getchar() devoluciones inty por una razón.

    – JuanLM

    12/02/2016 a las 17:45


  • @JudismarJunior, la respuesta aceptada tiene una excelente manera de expresar esto: asumiendo caracteres de 8 bits, está tratando de representar 257 símbolos con un tipo capaz de representar solo 256 símbolos. Necesita 256 caracteres + EOF. Int puede representar esos.

    – Ilja Everila

    13 de febrero de 2016 a las 10:08

  • @Ilja, lo tengo ahora. Gracias de nuevo.

    –Judismar Arpini Junior

    13 de febrero de 2016 a las 13:37

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad