Può sizeof (int) essere mai 1 in un’implementazione ospitata?

La mia opinione è che un’implementazione C non può soddisfare le specifiche di alcune funzioni stdio (in particolare fputc / fgetc ) se sizeof(int)==1 , poiché l’ int deve essere in grado di contenere qualsiasi valore ansible di unsigned char o EOF (-1 ). Questo ragionamento è corretto?

(Ovviamente sizeof(int) non può essere 1 se CHAR_BIT è 8, a causa dell’intervallo minimo richiesto per int , quindi stiamo implicitamente parlando solo di implementazioni con CHAR_BIT>=16 , ad esempio DSP, dove le implementazioni tipiche sarebbero un’implementazione indipendente piuttosto che un’implementazione ospitata, e quindi non richiesto per fornire stdio .)

Modifica : dopo aver letto le risposte e alcuni riferimenti ai collegamenti, alcuni pensieri su come potrebbe essere valido per un’implementazione ospitata avere sizeof(int)==1 :

Innanzitutto, alcune citazioni:

7.19.7.1 (2-3):

Se l’indicatore di fine del stream di input puntato dal stream non è impostato e un carattere successivo è presente, la funzione fgetc ottiene quel carattere come un carattere senza segno convertito in un valore int e fa avanzare l’indicatore di posizione del file associato per lo stream (se definito).

Se è impostato l’indicatore di fine del stream, o se il stream è alla fine, viene impostato l’indicatore di fine del stream e la funzione fgetc restituisce EOF. Altrimenti, la funzione fgetc restituisce il carattere successivo dal stream di input a cui punta il stream. Se si verifica un errore di lettura, viene impostato l’indicatore di errore per il stream e la funzione fgetc restituisce EOF.

7.19.8.1 (2):

La funzione fread legge, nell’array puntato da ptr, fino a elementi nmemb le cui dimensioni sono speci fi cate per dimensione, dal stream puntato al stream. Per ogni object, le chiamate di dimensioni vengono effettuate alla funzione fgetc e i risultati memorizzati, nell’ordine letto, in una matrice di caratteri non firmati che si sovrappongono esattamente all’object. L’indicatore di posizione del file per lo stream (se definito) viene anticipato dal numero di caratteri letti correttamente.

Pensieri:

  • La lettura dei valori di unsigned char al di fuori dell’intervallo di int potrebbe semplicemente avere un comportamento definito dall’implementazione non definito nell’implementazione. Ciò è particolarmente inquietante, poiché significa che l’utilizzo di fwrite e di fread per memorizzare strutture binarie (che mentre risulta in file non portatili, si suppone che sia un’operazione eseguibile su una singola implementazione) potrebbe sembrare funzionare ma fallire silenziosamente. essenzialmente si traduce sempre in un comportamento indefinito . Accetto che un’implementazione non abbia un filesystem utilizzabile, ma è molto più difficile accettare che un’implementazione possa avere un filesystem che invoca automaticamente i demoni nasali non appena si tenta di usarlo, e non c’è modo di determinare che sia inutilizzabile. Ora che mi rendo conto che il comportamento è definito dall’implementazione e non indefinito, non è poi così sconvolgente, e penso che potrebbe essere un’implementazione valida (anche se indesiderata).

  • Un’implementazione sizeof(int)==1 potrebbe semplicemente definire il filesystem vuoto e di sola lettura. Quindi non sarebbe ansible che un’applicazione possa leggere qualsiasi dato scritto da solo, solo da un dispositivo di input su stdin che potrebbe essere implementato in modo da fornire solo valori char positivi che si adattano a int .

Modifica (di nuovo): Dalla motivazione C99, 7.4:

EOF è tradizionalmente -1, ma può essere un numero intero negativo e quindi distinguibile da qualsiasi codice di carattere valido .

Ciò sembra indicare che sizeof(int) non può essere 1, o almeno che tale era l’intenzione del comitato.

È ansible che un’implementazione soddisfi i requisiti di interfaccia per fgetc e fputc anche se sizeof(int) == 1 .

L’interfaccia per fgetc dice che restituisce il carattere letto come un unsigned char convertito in un int . Da nessuna parte si dice che questo valore non può essere EOF anche se l’aspettativa è chiaramente che le letture valide “di solito” restituiscono valori positivi. Naturalmente, fgetc restituisce EOF su un errore di lettura o di fine stream, ma in questi casi viene impostato anche l’indicatore di errore del file o l’indicatore di fine del file (rispettivamente).

Allo stesso modo, da nessuna parte si dice che non è ansible passare EOF a fputc patto che ciò accada in coincidenza con il valore di un unsigned char convertito in un int .

Ovviamente il programmatore deve stare molto attento su tali piattaforms. Questo potrebbe non fare una copia completa:

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

Invece, dovresti fare qualcosa come (non testato!):

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

Ovviamente, le piattaforms in cui si avranno problemi reali sono quelle in cui sizeof(int) == 1 e la conversione da unsigned char a int non è un’iniezione. Credo che ciò avverrebbe necessariamente nel caso di piattaforms che utilizzano il segno e la grandezza o complementi per la rappresentazione di interi con segno.

Ricordo esattamente questa stessa domanda su comp.lang.c circa 10 o 15 anni fa. Cercandolo, ho trovato una discussione più attuale qui:

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

Penso che ci siano due fatti risultanti:

(a) Possono esserci implementazioni in cui non è ansible una stretta conformità. Ad esempio sizeof (int) == 1 con valori negativi di un complemento o di grandezza del segno o bit di riempimento nel tipo int, ovvero non tutti i valori di char senza segno possono essere convertiti in un valore int valido.

(b) L’idioma tipico ((c=fgetc(in))!=EOF) non è portabile (eccetto per CHAR_BIT == 8), poiché EOF non deve essere un valore separato.

Non credo che lo standard C richieda direttamente che EOF sia distinto da qualsiasi valore che possa essere letto da un stream. Allo stesso tempo, sembra dare per scontato che lo sarà. Alcune parti dello standard hanno requisiti in conflitto che dubito possano essere soddisfatti se EOF è un valore che può essere letto da un stream.

Ad esempio, considera ungetc . Da un lato, la specifica dice (§7.19.7.11):

La funzione ungetc spinge il carattere specificato da c (convertito in un char unsigned) nel stream di input puntato da stream. I caratteri respinti verranno restituiti dalle letture successive su quel stream nell’ordine inverso della loro spinta. […] Un carattere di pushback è garantito.

D’altra parte, dice anche:

Se il valore di c è uguale a quello della macro EOF, l’operazione non riesce e il stream di input non viene modificato.

Quindi, se EOF è un valore che può essere letto dallo stream, e (ad esempio) lo leggiamo dallo stream, e immediatamente usiamo ungetc per rimettere EOF nello stream, otteniamo un enigma: la chiamata è “garantita” per avere successo, ma anche esplicitamente richiesto di fallire.

A meno che qualcuno non veda un modo per conciliare questi requisiti, mi rimane un dubbio considerevole sul fatto che una tale implementazione possa essere conforms.

Nel caso in cui qualcuno si preoccupi, N1548 (la bozza attuale del nuovo standard C) mantiene gli stessi requisiti.

Non sarebbe sufficiente se un char nominale che condividesse un motivo con EOF fosse definito come non sensoriale? Se, ad esempio, CHAR_BIT era 16 ma tutti i valori consentiti occupavano solo i 15 bit meno significativi (assumendo un complemento a 2s della rappresentazione int di sign-magnitude). O tutto ciò che è rappresentabile in un char ha significato in quanto tale? Confesso che non lo so.

Certo, sarebbe una strana bestia, ma lasciamo che la nostra immaginazione vada qui, giusto?

R .. mi ha convinto che questo non reggerà. Poiché un’implementazione ospitata deve implementare stdio.h e se fwrite deve essere in grado di incollare interi sul disco, fgetc può restituire qualsiasi modello di bit che si adatterebbe a un char e che non deve interferire con il ritorno di EOF. QED.

Penso che tu abbia ragione. Tale implementazione non è in grado di distinguere un valore di char non firmato legittimo da EOF quando si utilizza fgetc / fputc su flussi binari.

Se ci sono tali implementazioni ( questa discussione sembra suggerire che ci sono), non sono strettamente conformi. È ansible avere un’implementazione indipendente con sizeof (int) == 1 .

Un’implementazione indipendente (C99 4) deve solo supportare le funzionalità della libreria standard come specificato in queste intestazioni: , , , , , e . (Nota n. ). Freestanding potrebbe avere più senso per un DSP o altro dispositivo incorporato comunque.

Non ho molta familiarità con C99, ma non vedo nulla che dice che fgetc deve produrre l’intera gamma di valori di char . Il modo ovvio per implementare lo stdio su un sistema del genere sarebbe inserire 8 bit in ogni char , indipendentemente dalla sua capacità. Il requisito di EOF è

EOF

che si espande a un’espressione costante intera, con tipo int e un valore negativo, che viene restituito da diverse funzioni per indicare la fine del file, cioè, non più input da un stream

La situazione è analoga a wchar_t e wint_t . Nella 7.24.1 / 2-3 che definisce wint_t e WEOF , la nota 278 dice

wchar_t e wint_t possono essere lo stesso tipo intero.

che sembrerebbe garantire che il controllo di gamma “soft” sia sufficiente a garantire che *EOF non sia nel set di caratteri.

Modificare:

Ciò non consentirebbe flussi binari, poiché in questo caso fputc e fgetc sono necessari per eseguire alcuna trasformazione. (7.19.2 / 3) I flussi binari non sono facoltativi; solo la loro distinzione dai flussi di testo è facoltativa. Quindi sembrerebbe che ciò renda questa implementazione non conforms. Sarebbe comunque perfettamente utilizzabile, purché non si tenti di scrivere dati binari al di fuori dell’intervallo a 8 bit.

Si presuppone che l’EOF non possa essere un carattere reale nel set di caratteri. Se lo permetti, sizeof (int) == 1 è OK.

Il compilatore TI C55x che sto utilizzando ha un char a 16 bit e 16 bit int e include una libreria standard. La libreria assume semplicemente un set di caratteri a otto bit, in modo che quando interpretato come un carattere come char di valore> 255 non sia definito; e quando si scrive su un dispositivo di stream a 8 bit, gli 8 bit più significativi vengono scartati: ad esempio, quando si scrive sull’unità UART, solo gli 8 bit più bassi vengono trasferiti al registro a scorrimento e in uscita.