Pode o sizeof (int) ser 1 em uma implementação hospedada?

Minha opinião é que uma implementação C não pode satisfazer a especificação de certas funções stdio (particularmente fputc / fgetc ) se sizeof(int)==1 , já que o int precisa ser capaz de manter qualquer valor possível de unsigned char ou EOF (-1 ). Este raciocínio está correto?

(Obviamente, sizeof(int) não pode ser 1 se CHAR_BIT for 8, devido ao intervalo mínimo requerido para int , então estamos implicitamente falando apenas sobre implementações com CHAR_BIT>=16 , por exemplo DSPs, em que implementações típicas seriam uma implementação independente em vez de uma implementação hospedada e, portanto, não é necessário fornecer stdio .)

Edit : Depois de ler as respostas e algumas referências de links, algumas reflexões sobre como pode ser válido para uma implementação hospedada ter sizeof(int)==1 :

Primeiro, algumas citações:

7.19.7.1 (2-3):

Se o indicador de fim de linha para o stream de input apontado por stream não estiver definido e um próximo caractere estiver presente, a function fgetc obtém esse caractere como um caractere não assinado convertido para um int e avança o indicador de posição de arquivo associado para o stream (se definido).

Se o indicador de fim de linha para o stream estiver definido, ou se o stream estiver no fim do arquivo, o indicador de fim de arquivo para o stream é ajustado e a function fgetc retorna EOF. Caso contrário, a function fgetc retorna o próximo caractere do stream de input apontado pelo stream. Se ocorrer um erro de leitura, o indicador de erro do stream será definido e a function fgetc retornará EOF.

7.19.8.1 (2):

A function fread lê, no array apontado por ptr, até os elementos nmemb cujo tamanho é especificado por tamanho, a partir do stream apontado pelo stream. Para cada object, as chamadas de tamanho são feitas para a function fgetc e os resultados armazenados, na ordem lida, em uma matriz de caracteres não assinados sobrepondo exatamente o object. O indicador de posição do arquivo para o stream (se definido) é avançado pelo número de caracteres lidos com sucesso.

Pensamentos:

  • A leitura de valores unsigned char fora do intervalo int poderia simplesmente ter um comportamento definido pela implementação indefinido na implementação. Isso é particularmente perturbador, pois significa que usar fwrite e fread para armazenar estruturas binárias (que, embora resultem em arquivos não-portáteis, supostamente é uma operação que pode ser executada portavelmente em qualquer implementação) pode parecer funcionar, mas falhar silenciosamente. essencialmente sempre resulta em comportamento indefinido . Eu aceito que uma implementação pode não ter um sistema de arquivos utilizável, mas é muito mais difícil aceitar que uma implementação possa ter um sistema de arquivos que invoca automaticamente demônios nasais assim que você tentar usá-lo, e nenhuma maneira de determinar que ele está inutilizável. Agora que percebo que o comportamento é definido pela implementação e não indefinido, não é tão perturbador, e acho que isso pode ser uma implementação válida (embora indesejável).

  • Uma implementação sizeof(int)==1 poderia simplesmente definir o sistema de arquivos como vazio e somente leitura. Então, não haveria nenhuma maneira de um aplicativo ler qualquer dado escrito por si só, apenas a partir de um dispositivo de input em stdin que poderia ser implementado de forma a dar apenas valores positivos de char que se encheckboxm no int .

Editar (de novo): A partir do argumento C99, 7.4:

EOF é tradicionalmente -1, mas pode ser qualquer inteiro negativo e, portanto, distinguível de qualquer código de caractere válido .

Isso parece indicar que sizeof(int) não pode ser 1, ou pelo menos que tal era a intenção do comitê.

É possível que uma implementação atenda aos requisitos de interface para fgetc e fputc mesmo se sizeof(int) == 1 .

A interface do fgetc diz que ele retorna o caractere lido como um unsigned char convertido para um int . Em nenhum lugar diz que esse valor não pode ser EOF , embora a expectativa seja claramente que a leitura válida “geralmente” retorna valores positivos. Naturalmente, o fgetc retorna EOF em uma falha de leitura ou no final do stream, mas nesses casos, o indicador de erro do arquivo ou o indicador de final de arquivo (respectivamente) também é configurado.

Da mesma forma, em nenhum lugar diz que você não pode passar EOF para fputc , desde que coincida com o valor de um unsigned char convertido para um int .

Obviamente, o programador tem que ter muito cuidado nessas plataformas. Isso pode não fazer uma cópia completa:

 void Copy(FILE *out, FILE *in) { int c; while((c = fgetc(in)) != EOF) fputc(c, out); } 

Em vez disso, você teria que fazer algo como (não testado!):

 void Copy(FILE *out, FILE *in) { int c; while((c = fgetc(in)) != EOF || (!feof(in) && !ferror(in))) fputc(c, out); } 

Claro, plataformas onde você terá problemas reais são aquelas em que sizeof(int) == 1 e a conversão de unsigned char para int não é uma injeção. Eu acredito que isso seria necessariamente o caso em plataformas usando sinal e magnitude ou um complemento para representação de números inteiros assinados.

Lembro-me exatamente desta mesma pergunta em comp.lang.c há 10 ou 15 anos atrás. Procurando por isso, eu encontrei uma discussão mais atual aqui:

http://groups.google.de/group/comp.lang.c/browse_thread/thread/9047fe9cc86e1c6a/cb362cbc90e017ac

Eu acho que existem dois fatos resultantes:

(a) Pode haver implementações em que a conformidade estrita não é possível. Por exemplo, sizeof (int) == 1 com valores negativos de preenchimento de um complemento ou magnitude de sinal ou bits de preenchimento no tipo int, ou seja, nem todos os valores char não assinados podem ser convertidos para um valor int válido.

(b) O idioma típico ((c=fgetc(in))!=EOF) não é portátil (exceto para CHAR_BIT == 8), já que o EOF não precisa ser um valor separado.

Não acredito que o padrão C exija diretamente que o EOF seja diferente de qualquer valor que possa ser lido de um stream. Ao mesmo tempo, parece dar como certo que será. Algumas partes do padrão possuem requisitos conflitantes que duvido que possam ser atendidos se o EOF for um valor que possa ser lido em um stream.

Por exemplo, considere ungetc . Por um lado, a especificação diz (§7.19.7.11):

A function ungetc envia o caractere especificado por c (convertido para um caractere não assinado) de volta ao stream de input apontado pelo stream. Caracteres empurrados para trás serão retornados por leituras subsequentes nesse stream na ordem inversa de seu envio. […] Um personagem de pushback é garantido.

Por outro lado, também diz:

Se o valor de c for igual ao da macro EOF, a operação falhará e o stream de input permanecerá inalterado.

Então, se EOF é um valor que pode ser lido do stream, e (por exemplo) nós lemos do stream, e imediatamente usamos ungetc para colocar EOF de volta no stream, nós temos um enigma: a chamada é “garantida” para ter sucesso, mas também explicitamente necessário para falhar.

A menos que alguém consiga enxergar uma maneira de conciliar esses requisitos, ficarei com uma dúvida considerável sobre se tal implementação pode se ajustar.

No caso de alguém se importar, o N1548 (atual versão do novo padrão C) mantém os mesmos requisitos.

Não seria suficiente se um char nominal que compartilhava um padrão de bits com EOF fosse definido como não-sensitivo? Se, por exemplo, CHAR_BIT fosse 16, mas todos os valores permitidos ocupavam apenas os 15 bits menos significativos (suponha um complemento de 2s da representação int de magnitude de sinal). Ou deve tudo representável em um char ter significado como tal? Eu confesso que não sei.

Claro, isso seria uma besta estranha, mas estamos deixando nossas imaginações aqui, certo?

R .. me convenceu de que isso não vai aguentar. Como uma implementação hospedada deve implementar stdio.h se fwrite é capaz de colocar números inteiros no disco, então fgetc poderia retornar qualquer padrão de bit que coubesse em um char , e isso não deveria interferir no retorno de EOF. QED.

Eu acho que você está certo. Tal implementação não pode distinguir um valor char não assinado legítimo de EOF ao usar fgetc / fputc em streams binários.

Se existem implementações desse tipo ( esta discussão parece sugerir que existem), elas não estão estritamente em conformidade. É possível ter uma implementação independente com sizeof (int) == 1 .

Uma implementação independente (C99 4) precisa apenas suportar os resources da biblioteca padrão, conforme especificado nestes headers: , , , , , e . (Note não ). O autoportante pode fazer mais sentido para um DSP ou outro dispositivo incorporado de qualquer maneira.

Não estou tão familiarizado com o C99, mas não vejo nada que diga que o fgetc deva produzir o intervalo completo de valores de char . A maneira óbvia de implementar o stdio em tal sistema seria colocar 8 bits em cada char , independentemente de sua capacidade. O requisito do EOF é

EOF

que se expande para uma expressão constante de inteiro, com o tipo int e um valor negativo, que é retornado por várias funções para indicar o fim do arquivo, ou seja, não há mais input de um stream

A situação é análoga a wchar_t e wchar_t . Em 7.24.1 / 2-3 definindo wint_t e WEOF , a nota de rodapé 278 diz

wchar_t e wchar_t podem ser do mesmo tipo inteiro.

o que parece garantir que a verificação de intervalo “soft” é suficiente para garantir que *EOF não esteja no conjunto de caracteres.

Editar:

Isso não permitiria streams binários, pois nesse caso, fputc e fgetc são obrigados a não realizar nenhuma transformação. (7.19.2 / 3) Fluxos binários não são opcionais; somente sua distinção de streams de texto é opcional. Então, parece que isso torna tal implementação não compatível. Ainda assim seria perfeitamente utilizável, desde que você não tente gravar dados binários fora do intervalo de 8 bits.

Você está assumindo que o EOF não pode ser um caractere real no conjunto de caracteres. Se você permitir isso, então sizeof (int) == 1 está OK.

O compilador TI C55x que estou usando tem um caractere de 16 bits e um int de 16 bits e inclui uma biblioteca padrão. A biblioteca simplesmente assume um conjunto de caracteres de oito bits, de modo que, quando interpretado como um caractere como caractere de valor> 255, não esteja definido; e ao gravar em um dispositivo de stream de 8 bits, os 8 bits mais significativos são descartados: Por exemplo, quando gravados no UART, somente os 8 bits inferiores são transferidos para o registrador de deslocamento e saída.