Diferença entre int e char em getchar / fgetc e putchar / fputc?

Eu estou tentando aprender C sozinho e estou meio confuso com getchar e 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; } 

A function da biblioteca C int putchar(int c) escreve um caractere (um caracter não assinado) especificado pelo argumento char para stdout.

A function da biblioteca C int getchar(void) obtém um caractere (um caracter não assinado) de stdin. Isso é equivalente a getc com stdin como seu argumento.

Significa putchar() aceita int e char ou qualquer um deles e para getchar() devemos usar um int ou char ?

TL; DR:

  • char c; c = getchar(); está errado, quebrado e com bugs .
  • int c; c = getchar(); está correto .

Isso se aplica também ao getc e ao fgetc , se não mais, porque geralmente se lê até o final do arquivo.


Sempre armazene o valor de retorno de getchar ( fgetc , getc …) (e putchar ) inicialmente em uma variável do tipo int .

O argumento para putchar pode ser qualquer um dos char int , char , signed char ou unsigned char ; o seu tipo não importa, e todos eles funcionam da mesma forma, mesmo que um possa resultar em números inteiros positivos e outros em números negativos sendo passados ​​para os caracteres acima e incluindo \200 (128).


O motivo pelo qual você deve usar int para armazenar o valor de retorno de ambos getchar e putchar é que quando a condição de fim de arquivo é atingida (ou ocorre um erro de E / S), ambos retornam o valor da macro EOF que é uma constante inteira negativa, (normalmente -1 ) .

Para getchar , se o valor de retorno não for EOF , será o unsigned char leitura unsigned char zerado para um int . Ou seja, assumindo caracteres de 8 bits, os valores retornados podem ser 0255 ou o valor da macro EOF ; novamente assumindo char de 8 bits, não há como compactar esses 257 valores distintos em 256, de modo que cada um deles possa ser identificado exclusivamente.


Agora, se você armazenou no char , o efeito dependerá se o tipo de caractere é assinado ou não assinado por padrão ! Isso varia de compilador para compilador, arquitetura para arquitetura. Se char é assinado e supondo que EOF é definido como -1 , então EOF e caractere '\377' na input comparariam igual a EOF ; eles seriam estendidos com sinal para (int)-1 .

Por outro lado, se o char não estiver assinado (como é padrão nos processadores ARM, incluindo os sistemas PI Raspberry ; e também parece ser verdadeiro para o AIX ), não há nenhum valor que possa ser armazenado em c que seja igual a -1 ; incluindo EOF ; em vez de invadir o EOF , seu código produziria um único caractere \377 .

O perigo aqui é que, com char assinados, o código parece estar funcionando corretamente, mesmo que ainda esteja terrivelmente quebrado – um dos valores de input legais é interpretado como EOF . Além disso, C89, C99, C11 não exigem um valor para EOF ; diz apenas que EOF é uma constante inteira negativa; assim, em vez de -1 , poderia ser dito -224 em uma implementação específica, o que faria com que os espaços se comportassem como EOF .

gcc tem o switch -funsigned-char que pode ser usado para tornar o char não assinado nas plataformas onde o padrão é assinado:

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

Agora corremos com char assinado:

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

Parece estar funcionando certo. Mas com unsigned char :

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

Ou seja, tentei pressionar Ctrl-D muitas vezes, mas um foi impresso para cada EOF vez de quebrar o loop.

Agora, novamente, para o caso de char assinado, ele não pode distinguir entre o char 255 e o EOF no Linux, quebrando-o para dados binários e tal:

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

Apenas a primeira parte até a fuga de \0377 foi escrita para stdout.


Tenha em atenção que as comparações entre constantes de caracteres e um int contendo o valor de caractere não assinado podem não funcionar conforme esperado (por exemplo, a constante de caracteres 'ä' na ISO 8859-1 significaria o valor assinado -28 . Assumindo que você escreve código que lia input até 'ä' na página de códigos ISO 8859-1, você faria

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

Devido à promoção de número inteiro, todos os valores de char encheckboxm em um int e são promovidos automaticamente em chamadas de function, assim você pode atribuir um char int , char , signed char ou unsigned char a putchar como argumento (não para armazenar seu valor de retorno) e funcionaria como esperado.

O valor real passado no inteiro pode ser positivo ou até negativo; por exemplo, a constante de caractere \377 seria negativa em um sistema de caracteres de 8 bits em que char é assinado; no entanto putchar (ou fputc na verdade) vai lançar o valor para um char não assinado. C11 7.21.7.3p2 :

2 A function fputc escreve o caractere especificado por c (convertido em um caractere não assinado) no stream de saída apontado pelo stream […]

(ênfase minha)

fputc seja, o fputc será garantido para converter o c dado como se por (unsigned char)c

Sempre use int para salvar o caractere de getchar() como constante EOF é do tipo int . Se você usar char , a comparação com o EOF não está correta.

Você pode seguramente passar char para putchar() já que ele será promovido para int automaticamente.

Nota : Tecnicamente, o uso do char funcionará na maioria dos casos, mas você não poderá ter o caractere 0xFF, pois eles serão interpretados como EOF devido à conversão de tipo. Para cobrir todos os casos, use sempre int . Como @Ilja coloca – int é necessário para representar todos os 256 valores de caracteres possíveis e o EOF , que é 257 valores possíveis no total, que não podem ser armazenados no tipo char .