2009-03-17 20 views

risposta

26

Qualora un buffer di byte essere firmato char o unsigned char o semplicemente un char buffer? Qualche differenza tra C e C++?

Una piccola differenza nel modo in cui la lingua lo tratta. A enorme differenza nel modo in cui la convenzione lo tratta.

  • char = ASCII (o UTF-8, ma il signedness si mette di mezzo ci) testuali dati
  • unsigned char = byte
  • signed char = usato raramente

E non c'è codice che si basa su su tale distinzione. Solo una o due settimane fa ho riscontrato un errore relativo al danneggiamento dei dati JPEG perché era passato alla versione char* della nostra funzione di codifica Base64 — che "utilmente" ha sostituito tutti gli UTF-8 non validi nella "stringa". Il passaggio a BYTE aka unsigned char era tutto ciò che serviva per risolverlo.

+5

Allora perché gli iostream del C++ usano 'char *' invece di 'unsigned char *' per rappresentare i buffer dei dati durante la lettura e la scrittura di stream binari attraverso i metodi 'read' e' write'? : P – BarbaraKwarc

+1

char non è che raramente. In JNI (interfaccia java nativa, NDK 14.1), il char firmato è definito come jbyte. – r0ng

+3

wtf fa java ha a che fare con questo (ugh) – developerbmw

0

Se si recupera un elemento in una variabile più ampia, sarà ovviamente esteso al segno o no.

0

dovrebbero e ... tendo a preferire non firmato, dal momento che ci si sente più "grezzo", meno invitante per dire "hey, questo è solo un mucchio di piccoli ints", se voglio sottolineare il binario -ness dei dati.

Non credo di aver mai usato un signed char esplicito per rappresentare un buffer di byte.

Ovviamente, una terza opzione consiste nel rappresentare il buffer come void * il più possibile. Molte funzioni di I/O comuni funzionano con void *, quindi a volte la decisione su quale tipo di intero da utilizzare può essere completamente incapsulata, il che è bello.

+1

Il problema è che non si può usare molto char senza che venga promosso. (char) 0xFF! = (unsigned char) 0xFF. I byte sono gli stessi ma non sono uguali. –

4

È meglio definirlo come char senza segno. Infatti il ​​tipo BYTE Win32 è definito come char senza segno. Non c'è differenza tra C & C++ tra questo.

+1

Utilizzare un caso particolare (Win32) per dimostrare che la dichiarazione generale non è l'idea migliore. – BarbaraKwarc

0

Diversi anni fa ho avuto un problema con un'applicazione di console C++ che ha stampato caratteri colorati per valori ASCII superiori a 128 e questo è stato risolto passando da char a unsigned char, ma penso che sia stato risolvibile pur mantenendo il char type, anch'esso .

Per ora, la maggior parte delle funzioni di C/C++ utilizza char e ora conosco molto meglio entrambe le lingue, quindi utilizzo char nella maggior parte dei casi.

12

Dipende.

Se il buffer è destinato a contenere il testo, allora probabilmente ha senso dichiararlo come una serie di char e lasciare che la piattaforma decida per voi se è stato firmato o non firmato per impostazione predefinita. Ad esempio, questo ti darà il minimo problema nel passare i dati dentro e fuori dalla libreria di runtime dell'implementazione.

Se il buffer è destinato a contenere dati binari, dipende da come si intende utilizzarlo. Ad esempio, se i dati binari sono in realtà una serie di campioni di dati comprendenti misurazioni ADC a punto fisso a 8 bit con segno, è consigliabile utilizzare signed char.

Nella maggior parte dei casi reali, il buffer è proprio questo, un buffer, e non ti interessa davvero i tipi dei singoli byte perché hai riempito il buffer in un'operazione di massa e stai per passare off ad un parser per interpretare la complessa struttura dati e fare qualcosa di utile. In tal caso, dichiaralo nel modo più semplice.

0

Ti interessa davvero? Se non lo fai, basta usare il default (char) e non ingombrare il tuo codice con argomenti non importanti. Altrimenti, i futuri manutentori rimarranno a chiedersi perché hai usato il segno (o non firmato). Rendi la vita più semplice.

+5

Non sono d'accordo. Se incontro una serie di caratteri (firmati), potrei essere incline a pensare che detenga in qualche modo dati testuali. –

+1

D'accordo con Dave VdE – dcw

+1

E perché non può un array di caratteri unsigned contenere dati testuali? La firma di default del char normale differisce tra le architetture, ma le firme libc delle funzioni di stringa sono sempre le stesse. –

9

Se in realtà è un buffer di byte da 8 bit, anziché una stringa nelle impostazioni internazionali predefinite della macchina, utilizzare lo uint8_t. Non che ci siano molte macchine in giro dove un char non è un byte (o un byte un ottetto), ma rendere l'istruzione "questo è un buffer di ottetti" piuttosto che "questa è una stringa" è spesso una documentazione utile.

+0

L'ho passato, e in teoria è bello, ma crea molti problemi se si passano questi dati alle funzioni standard di C o POSIX (lettura/scrittura file/socket). –

+4

POSIX lettura/scrittura prendere un buffer * vuoto. Le funzioni POSIX che prevedono un char * (ad es. L'argomento path di open()) si aspettano una stringa, non un buffer di byte. –

3

Per la massima portabilità utilizzare sempre il carattere senza segno.Ci sono un paio di casi in cui questo potrebbe entrare in gioco. Vengono subito in mente dati serializzati condivisi su sistemi con tipi diversi di endian. Quando si esegue lo spostamento o il mascheramento dei bit, i valori sono un altro.

5

Si consiglia di utilizzare uno char o unsigned char ma mai firmato char. Lo standard ha la seguente in 3,9/2

Per qualsiasi oggetto (diverso da un sotto-oggetto classe base) di tipo POD T, se l'oggetto contiene un valore valido di tipo T, il sottostante byte (1.7) che compongono l'oggetto può essere copiati in un array di char o unsigned char.If il contenuto matrice di char o unsigned char è copiato nuovamente nell'oggetto, l'oggetto procede successivamente tenere la sua valore originale.

47

Se si desidera archiviare dati binari arbitrari, è necessario utilizzare unsigned char. È l'unico tipo di dati a garantire che non abbia bit di riempimento dallo standard C. Ogni altro tipo di dati può contenere bit di riempimento nella sua rappresentazione dell'oggetto (cioè quella che contiene tutti i bit di un oggetto, anziché solo quelli che determinano un valore). Lo stato dei bit del padding non è specificato e non viene utilizzato per memorizzare i valori. Quindi, se si legge usando char alcuni dati binari, le cose verrebbero ridotte all'intervallo di valori di un carattere (interpretando solo i bit di valore), ma potrebbero esserci ancora bit che vengono semplicemente ignorati ma ancora presenti e letti da memcpy. Proprio come i bit di riempimento in oggetti struct reali. Tipo unsigned char è garantito per non contenere quelli. Ciò risulta dal 5.2.4.2.1/2 (C99 TC2, n1124 qui):

Se il valore di un oggetto di tipo char viene trattato come un intero con segno se impiegati in un espressione, il valore di CHAR_MIN è la stessa di quella di SCHAR_MIN e il valore di CHAR_MAX deve essere uguale a quello di SCHAR_MAX. In caso contrario, il valore di CHAR_MIN deve essere 0 e il valore di CHAR_MAX deve essere uguale a quello di UCHAR_MAX. Il valore deve essere uguale UCHAR_MAX2^CHAR_BIT − 1

dall'ultima frase ne consegue che non v'è più spazio per eventuali bit di riempimento. Se si utilizza char come tipo di buffer, si ha anche il problema di overflow: Assegnare qualsiasi valore esplicitamente a un tale elemento che si trova nell'intervallo di 8 bit, quindi è possibile che tale assegnazione sia corretta, ma non all'interno del intervallo di char, che è CHAR_MIN .. CHAR_MAX, una tale overflow di conversione e causa risultati definiti dall'implementazione, incluso aumento di segnali.

Anche in caso di problemi per quanto riguarda il sopra probabilmente non mostrare nelle implementazioni reali (sarebbe molto scarsa qualità dell'attuazione ), si è meglio usare il tipo fin dall'inizio in poi, che è unsigned char.

Per le stringhe, tuttavia, il tipo di dati scelto è char, che sarà compreso dalle stringhe e dalle funzioni di stampa.Usare signed char per questi scopi mi sembra una decisione sbagliata.

Per ulteriori informazioni, leggere this proposal che contengono una correzione per una versione successiva dello standard C che alla fine richiederà signed char non avere alcun bit di riempimento. È già incorporato nello working paper.

+0

B-ma C99 6.2.6.2 dice "il carattere firmato non deve avere alcun bit di riempimento" – Ivan

+7

Dimenticate C. '[C++ 11: 3.9.1/1]:' [..] _Un carattere, un carattere firmato e un char senza segno occupa la stessa quantità di memoria e ha gli stessi requisiti di allineamento (3.11); cioè, hanno la stessa rappresentazione dell'oggetto. Per i tipi di carattere, tutti i bit della rappresentazione dell'oggetto partecipano alla rappresentazione del valore. [[]] Questo non suggerisce che tutti i tre tipi di caratteri abbiano, come minimo, il padding _same_? E lo interpreto ulteriormente per significare che nessuno di loro ne ha. –

+0

(vedere http://stackoverflow.com/a/21176278/560648) –

2

La scelta di int8_t vs uint8_t è simile a quando si confronta un ptr con NULL.


Da un punto di vista funzionale, il confronto NULL è lo stesso che si confrontano a 0 perché NULL è un #define per 0.

Ma, personalmente, da un punto di vista stile di codifica, scelgo per confrontare i miei puntatori NULL perché la # define NULL connota alla persona mantenere il codice che si sta verificando per un brutto puntatore ...

VS

quando qualcuno vede un confronto a 0 si connota che siete controllando per un valore specifico.


Per il motivo sopra, vorrei usare uint8_t.

-1

Se menti al compilatore, ti punirà.

Se il buffer contiene dati che passano e non li manipolerai in alcun modo, non importa.

Tuttavia, se si deve operare sul contenuto del buffer, la corretta dichiarazione del tipo renderà il codice più semplice. No "int val = buf [i] & 0xff;" senza senso.

Quindi, pensa a cosa sono effettivamente i dati e come devi usarli.

0
typedef char byte; 

Ora è possibile effettuare la matrice sia di byte s. È ovvio per tutti cosa intendi e non perdi alcuna funzionalità.

So che è un po 'sciocco, ma rende il vostro codice letto al 100% come previsto.

+3

È ** non ** ovvio per i programmatori di Windows che sono abituati a 'typedef unsigned char BYTE'. – dan04

+0

Come risponde la domanda? –

+3

Nel suo dominio, si riferisce ad esso come "un buffer di byte". La maggior parte delle risposte parla di ciò che è diverso nelle scelte disponibili. Ho preso l'approccio di spiegare, "Se ti riferisci ad esso come un 'byte', potrebbe essere meglio dattilografarlo in questo modo". 23 persone Questa è stata una domanda interessante, e 12 persone lo hanno STATO - è scioccante. Concordo sul fatto che il mio post non tenti di rispondere alla domanda dell'utente, ma sosterrò anche che altre risposte qui ignorano un aspetto dello sviluppo del software che stavo tentando di illuminare: come nominare i tipi di cose. –

Problemi correlati