paulo1205
(usa Ubuntu)
Enviado em 18/04/2020 - 13:05h
O material indicado infelizmente tem diversos problemas. Entre eles:
• Ao longo de todo o capítulo, ele consistentemente chama declarações de “sintaxe”. Por mais que declarações tenham de obedecer regras sintáticas da linguagem, as duas coisas não são sinônimas.
• A constante
EOF é apresentada como um possível valor de retorno de
fgetc() que só ocorre caso o fim do arquivo seja atingido. Na verdade, qualquer situação que impeça a leitura do próximo caráter produz
EOF como resultado. Chegar ao fim do arquivo é uma dessas situações, mas há também casos como uma falha de disco que ocorra durante a leitura do próximo bloco de dados e situações de erro em nível de sistema operacional, tais como um
socket ou
pipe marcado como não-blocante e que ainda não tem nenhum dado disponível para leitura, mas que pode vir a ter em um momento posterior.
• Apesar de ter explicado que
fgetc() retorna um valor do tipo
int para acomodar todos os possíveis caracteres (valores de 0 a 255, em máquinas cujo tipo
char tem oito bits) e também a sinalização de erro (
EOF, cujo valor é -1), todos os programas mostrados como exemplo usam variáveis do tipo
char para receber o valor retornado por
fgetc(), o que elimina completamente a possibilidade de detectar o valor
EOF de maneira confiável. Para que
EOF possa caber na variável, que só tem o espaço de um
char, alguns bits têm de ser truncados, e o resultado desse truncamento pode produzir um valor que é idêntico ao de um caráter válido (num arquivo de texto em um sistema com codificação Windows CP1252 ou ISO-8859-1, esse caráter é o “Y minúsculo com trema”, ou
'ÿ'; num arquivo binário, é o
byte com valor
'\xff').
Depois do truncamento, que gera a ambiguidade que já é um problema por si só, existe o problema da comparação desse valor com
EOF, que provoca a conversão inversa, de
char para
int. Essa conversão pode cair em um dos seguintes casos.
• Se o tipo
char, por padrão (como nos nossos PCs com processadores Intel ou AMD) ou por força de alguma opção de compilação, for com sinal (i.e. representa valores entre -128 e +127, em máquinas em que o
char tem oito bits), então a ambiguidade mencionada acima pode interromper a leitura antes da hora, pois o caráter com valor
-1 (como o
'ÿ' ou o
byte '\xff') será convertido para inteiro como
-1, que é o mesmo valor de
EOF.
• Se o tipo
char, por padrão (como nos nossos celulares como processadores ARM) ou por força de alguma opção de compilação, for sem sinal (i.e. representa valores entre 0 e 255, em máquinas em que o
char tem oito bits), a ambiguidade mencionada acima também existe, mas o valor ambíguo é igual a
(char)255. Tal valor, ao ser convertido de volta para
int durante a operação de comparação, produzirá o valor inteiro
255, e portanto nunca será igual a
EOF, de modo que o laço de repetição nunca será interrompido, mesmo que
fgetc() repetidamente encontre o fim de arquivo ou algum erro.
• A declaração mostrada para
fscanf() está incorreta (e ele ainda a chama de “sintaxe“).
• Ao dizer, sobre
fscanf(), que “
[c]omo as outras, retorna EOF caso não tenha conseguido fazer a leitura”, ele erra de múltiplas maneiras. De cara, a que “outras” ele se refere? O plural não cabe porque, antes de
fscanf(), ele só falou de
fgetc(); depois, ele só fala de uma função que nem sequer pode retornar
EOF, já que o tipo de dados retornado não é compatível com tal valor. Além disso, não existe “a leitura” como operação atômica por parte de
fscanf(), e o valor de retorno vai depender do conteúdo da
string de formatação, podendo inclusive indicar sucesso parcial. A função só retorna
EOF em casos específicos, podendo inclusive não retornar
EOF mesmo que o fim de arquivo tenha sido encontrado. É importante ler uma documentação da função para conhecer esses casos (a documentação da
manpage da função no Linux é um exemplo de boa documentação).
• O autor não explica coisa alguma sobre a forma de passar argumentos que devem receber valores como parâmetros para
fscanf(). Apesar de ser uma frequente causa de dúvidas entre iniciantes e uma excelente oportunidade para explicar a passagem de referências como argumentos, o artigo simplesmente não fala coisa alguma a respeito (nem ali, nem em capítulos anteriores da mesma apostila).
• Na descrição de
fgets(), o autor menciona o uso de
gets(), que era uma função tão mal concebida que acabou sendo eliminada da biblioteca padrão do C no padrão de 2011 (e antes disso, no padrão de 1999, já era marcada como obsoleta, e os compiladores costumavam produzir alertas quando ela era usada). Que ela ainda seja mencionada no material do curso é, por si só, um problema.
• Ao dizer que “
[m]uitas vezes é interessante pegar uma linha inteira de um arquivo, principalmente se nesse arquivo existir (sic)
textos”, o autor pode ter cometido, além do erro de concordância, também um erro de categorização. Do ponto de vista do C, um arquivo é integralmente de texto ou integralmente binário, e a existência de linhas é justamente uma das características que definem um arquivo de texto (§7.21.2, parágrafo 2 do padrão do C de 2018). Do modo como ele escreveu, fica a dúvida de se ele imagina tratar partes de um arquivo binário como se fosse texto, o que é até possível, mas tem uma razoável possibilidade de não produzir muito bons resultados.
• A explicação do funcionamento da função diz que “
[a] função vai abrir o arquivo apontado por arq”, o que é falso. O arquivo já tem de estar aberto em modo de leitura, e a função recebe um ponteiro para o
stream já aberto.
• Ao longo da explicação de
fgets(), não fica suficientemente claro que a função retém o caráter de fim de linha após a leitura (se ele couber no
array de destino). Não que a descrição “
vai pegar do primeiro caractere até o new line” esteja incorreta, mas a menção anterior da antiga
gets() com praticamente a mesma descrição de funcionamento ajuda ainda mais criar a confusão, uma vez que
gets() descartava o caráter de fim de linha, em lugar de copiá-lo para o
array. Também contribui para causar confusão dizer a forma de
fgets() obter seus dados é “
igual aqueles formulários que preenchemos na internet”.
• Ainda a respeito de manter ou descarar o caráter de fim de linha, seria interessante mostrar como fazer o descarte, caso desejável ou necessário.
• Por fim, a sugestão de que verificar se o valor retornado por
fgets() é igual a
NULL é suficiente para indicar que se chegou ao final do arquivo não está correta. Outras situações também podem provocar o retorno de
NULL, e o fim de arquivo também pode já ter sido atingido, mas
fgets() ainda não refletir essa condição no valor de retorno (e isso é, inclusive, um dos motivos pelos quais a função repassa o caráter de fim de linha à
string, em lugar de o descartar).
... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)