Armazenando o caractere EOF (Fim do Arquivo) em um tipo de char
On Fevereiro 18, 2021 by admin Eu li na Linguagem de Programação C de Dennis Ritchie livro que int
deve ser usado para uma variável para conter EOF – para torná-lo suficientemente grande para que possa conter o valor EOF – não char
. Mas o código a seguir funciona bem:
#include<stdio.h> main() { char c; c=getchar(); while(c!=EOF) { putchar(c); c=getchar(); } }
Quando houver não é mais entrada, getchar
retorna EOF. E no programa acima, a variável c
, com tipo char, é capaz de mantê-lo com sucesso.
Por que isso funciona? De acordo com a explicação no livro mencionado acima, o código não deve funcionar.
Comentários
Resposta
Seu código parece funcionar, porque as conversões de tipo implícitas acidentalmente acontecem para fazer a coisa certa.
getchar()
retorna um int
com um valor que se encaixa no intervalo de unsigned char
ou é EOF
( que deve ser negativo, geralmente é -1). Observe que EOF
em si não é um caractere, mas um sinal de que não há mais caracteres disponíveis.
Ao armazenar o resultado de getchar()
em c
, existem duas possibilidades. O tipo char
pode representar o valor, caso em que é o valor de c
. Ou o tipo char
não pode representar o valor. Nesse caso, não está definido o que acontecerá. Os processadores Intel apenas cortam os bits altos que não cabem no novo tipo (reduzindo efetivamente o valor do módulo 256 para char
), mas você não deve confiar nisso.
A próxima etapa é comparar c
com EOF
. Como EOF
é um int
, c
será convertido em int
também, preservando o valor armazenado em c
. Se c
puder armazenar o valor de EOF
, a comparação será bem-sucedida , mas se c
puder não armazenar o valor, a comparação falhará, porque houve uma perda irrecuperável de informações durante a conversão EOF
para digitar char
.
Parece que seu compilador escolheu fazer o tipo char
assinado e o valor de EOF
pequeno o suficiente para caber em char
. Se char
não tivesse sinal (ou se você tivesse usado unsigned char
), seu teste teria falhado, porque unsigned char
não pode “manter o valor de EOF
.
Observe também que há um segundo problema com seu código. Como EOF
não é um caractere em si, mas você o força em um tipo char
, é muito provável que haja um caractere por aí que é mal interpretado como sendo EOF
e para metade dos caracteres possíveis é indefinido se eles serão processados corretamente.
Comentários
- Coerção para digitar
char
valores fora do intervaloCHAR_MIN
..CHAR_MAX
vontade é necessária para produzir um valor Definido pela Implementação, produz um padrão de bits que a implementação define como uma representação de trap, ou levanta um sinal definido pela implementação. Na maioria dos casos, as implementações teriam que passe por muito trabalho extra para fazer qualquer coisa além de duas ' redução do complemento de s.Se as pessoas no Comitê de Padrões subscrevessem a ideia de que os compiladores deveriam ser encorajados a implementar comportamentos consistentes com os da maioria dos outros compiladores na ausência de razões para fazer o contrário … - … Eu consideraria isso coerção como sendo confiável (para não dizer que o código não deve ' documentar suas intenções, mas que
(signed char)x
deve ser considerado mais claro e seguro como((unsigned char)x ^ CHAR_MAX+1))-(CHAR_MAX+1)
.) Do jeito que está, eu não ' não vejo qualquer probabilidade de compiladores implementarem qualquer outro comportamento compatível com ' s padrão; o único perigo seria que o Padrão pudesse ser alterado para quebrar o comportamento no suposto interesse de " otimização ". - @supercat: o padrão é escrito de forma que nenhum compilador precise produzir código que tenha um comportamento que não seja suportado naturalmente pelo processador ao qual se destina. A maior parte do comportamento indefinido ocorre porque (no momento da redação do padrão) nem todos os processadores se comportavam de maneira consistente. Com os compiladores ficando mais maduros, os escritores de compiladores começaram a tirar vantagem do comportamento indefinido para fazer otimizações mais agressivas.
- Historicamente, a intenção do Padrão era principalmente como você descreve, embora o Padrão descreva alguns comportamentos em detalhes suficientes para exigir que os compiladores de algumas plataformas comuns gerem mais código do que seria necessário em uma especificação mais flexível. A coerção de tipo em
int i=129; signed char c=i;
é um desses comportamentos. Relativamente poucos processadores têm uma instrução que tornariac
iguali
quando ' s em o intervalo de -127 a +127 e produziria qualquer mapeamento consistente de outros valores dei
para valores no intervalo de -128 a +127 que diferiam de dois ' redução do complemento de s, ou … - … levantaria consistentemente um sinal em tais casos. Uma vez que o Padrão requer que as implementações produzam um mapeamento consistente ou levantem um sinal de forma consistente, as únicas plataformas onde o Padrão deixaria espaço para algo diferente de dois ' redução do complemento s seriam coisas como DSPs com hardware de saturação aritmética. Quanto à base histórica para comportamento indefinido, eu diria que o problema não é ' apenas com plataformas de hardware. Mesmo em uma plataforma onde o overflow se comportaria de uma maneira muito consistente, pode ser útil ter um compilador para interceptá-lo …
0xff
. Armazenando o resultado degetchar()
em umint
resolve esse problema. Sua pergunta é essencialmente igual à pergunta 12.1 no FAQ do comp.lang.c , que é um excelente recurso. (Além disso,main()
deve serint main(void)
, e não ' custaria adicionar umreturn 0;
antes do fechamento}
.)