uint8_t vs char senza segno


231

Qual è il vantaggio di usare uint8_tover unsigned charin C?

So che su quasi tutti i sistemi uint8_tè solo un typedef per unsigned char, quindi perché usarlo?

Risposte:


225

Documenta il tuo intento: memorizzerai piccoli numeri, piuttosto che un personaggio.

Inoltre sembra più bello se stai usando altri typedef come uint16_to int32_t.


1
Non era chiaro nella domanda originale se stessimo parlando di un tipo standard o meno. Sono sicuro che ci sono state molte varianti di questa convenzione di denominazione nel corso degli anni.
Mark Ransom,

8
Usare esplicitamente unsigned charo signed chardocumentare anche l'intento, poiché disadorno charè ciò che mostra che stai lavorando con i personaggi.
caf

9
Pensavo che un disadorno unsignedfosse unsigned intper definizione?
Mark Ransom,

5
@endolith, usare uint8_t per una stringa non è necessariamente sbagliato, ma è decisamente strano.
Mark Ransom,

5
@endolith, penso di poter fare un caso per uint8_t con il testo UTF8. In effetti, charsembra implicare un carattere, mentre nel contesto di una stringa UTF8, può essere solo un byte di un carattere multibyte. L'uso di uint8_t potrebbe chiarire che non ci si dovrebbe aspettare un carattere in ogni posizione, in altre parole che ogni elemento della stringa / matrice è un numero intero arbitrario sul quale non si dovrebbero fare ipotesi semantiche. Naturalmente tutti i programmatori C lo sanno, ma può spingere i principianti a porre le domande giuste.
TNE

70

Solo per essere pedanti, alcuni sistemi potrebbero non avere un tipo a 8 bit. Secondo Wikipedia :

È necessaria un'implementazione per definire tipi interi di larghezza esatta per N = 8, 16, 32 o 64 se e solo se ha un tipo che soddisfa i requisiti. Non è necessario definirli per nessun altro N, anche se supporta i tipi appropriati.

Così uint8_t non è garantito che esista, sebbene lo sarà per tutte le piattaforme in cui 8 bit = 1 byte. Alcune piattaforme incorporate potrebbero essere diverse, ma sta diventando molto raro. Alcuni sistemi possono definire i chartipi come 16 bit, nel qual caso probabilmente non ci sarà un tipo di 8 bit di alcun tipo.

Oltre a quello (minore) problema, la risposta di @Mark Ransom è la migliore secondo me. Usa quello che mostra più chiaramente a cosa stai utilizzando i dati.

Inoltre, suppongo che intendessi uint8_t(il typedef standard di C99 fornito stdint.hnell'intestazione) anziché uint_8(non parte di alcuno standard).


3
@caf, per pura curiosità: puoi collegarti alla descrizione di alcuni? So che esistono perché qualcuno ne ha menzionato uno (e collegato ai documenti dello sviluppatore per esso) in una discussione moderata comp.lang.c ++. Sul fatto che le garanzie di tipo C / C ++ siano troppo deboli, ma non riesco più a trovare quel thread ed è sempre utile per fare riferimento a ciò in discussioni simili :)
Pavel Minaev,

3
"Alcuni sistemi possono definire i tipi di caratteri come 16 bit, nel qual caso probabilmente non ci sarà un tipo di 8 bit di alcun tipo." - e nonostante alcune obiezioni errate da parte mia, Pavel ha dimostrato nella sua risposta che se char è di 16 bit, allora anche se il compilatore fornisce un tipo di 8 bit, non deve chiamarlo uint8_t(o darlo a quello). Questo perché il tipo a 8 bit avrebbe bit inutilizzati nella rappresentazione della memoria, che uint8_tnon devono avere.
Steve Jessop,

3
L'architettura SHARC ha parole a 32 bit. Vedi en.wikipedia.org/wiki/… per i dettagli.
BCran,

2
E i DSP C5000 di TI (che erano in OMAP1 e OMAP2) sono a 16 bit. Penso che per OMAP3 siano andati alla serie C6000, con un carattere a 8 bit.
Steve Jessop,

4
Scavando in N3242 - "Working Draft, Standard for Programming Language C ++", la sezione 18.4.1 <cstdint> sinossi dice - typedef unsigned integer type uint8_t; // optional Quindi, in sostanza, non è necessaria una libreria conforme allo standard C ++ per definire uint8_t (vedi il commento // opzionale )
sentieri notturni il

43

Il punto è scrivere codice indipendente dall'implementazione. unsigned charnon è garantito che sia un tipo a 8 bit. uint8_tè (se disponibile).


4
... se esiste su un sistema, ma sarà molto raro. +1
Chris Lutz,

2
bene se hai davvero avuto problemi con il codice che non si stava compilando su un sistema perché uint8_t non esisteva, potresti usare find e sed per cambiare automaticamente tutte le occorrenze di uint8_t in caratteri non firmati o in qualcosa di più utile per te.
Bazz,

2
@bazz - non se stai assumendo che sia un tipo a 8 bit che non puoi - ad esempio per decomprimere i dati impacchettati in modo bytewise da un sistema remoto. L'assunto implicito è che il motivo per cui uint8_t non esiste è su un processore in cui un carattere è superiore a 8 bit.
Chris Stratton,

lancia asserzione asserzione (sizeof (carattere senza segno) == 8);
bazz,

3
@bazz affermazione errata temo. sizeof(unsigned char)tornerà 1per 1 byte. ma se un char di sistema e int hanno le stesse dimensioni di, per esempio, a 16 bit, allora sizeof(int)tornerà anche1
Toby

7

Come hai detto, " quasi tutti i sistemi".

charè probabilmente uno dei meno propensi a cambiare, ma una volta che inizi a usare uint16_te gli amici, usare le uint8_tmiscele meglio e potrebbe persino far parte di uno standard di codifica.


7

Nella mia esperienza ci sono due posti in cui vogliamo usare uint8_t per indicare 8 bit (e uint16_t, ecc.) E dove possiamo avere campi più piccoli di 8 bit. Entrambe le aree sono dove lo spazio è importante e spesso abbiamo bisogno di esaminare un dump grezzo dei dati durante il debug e dobbiamo essere in grado di determinare rapidamente ciò che rappresenta.

Il primo è nei protocolli RF, specialmente nei sistemi a banda stretta. In questo ambiente potrebbe essere necessario racchiudere quante più informazioni possibili in un singolo messaggio. Il secondo è nella memoria flash in cui potremmo avere uno spazio molto limitato (come nei sistemi embedded). In entrambi i casi possiamo utilizzare una struttura di dati impaccata in cui il compilatore si occuperà dell'imballaggio e del disimballaggio per noi:

#pragma pack(1)
typedef struct {
  uint8_t    flag1:1;
  uint8_t    flag2:1;
  padding1   reserved:6;  /* not necessary but makes this struct more readable */
  uint32_t   sequence_no;
  uint8_t    data[8];
  uint32_t   crc32;
} s_mypacket __attribute__((packed));
#pragma pack()

Quale metodo usi dipende dal tuo compilatore. Potrebbe anche essere necessario supportare diversi compilatori con gli stessi file di intestazione. Ciò accade nei sistemi embedded in cui dispositivi e server possono essere completamente diversi, ad esempio potresti avere un dispositivo ARM che comunica con un server Linux x86.

Ci sono alcune avvertenze sull'utilizzo di strutture impaccate. Il più grande gotcha è che devi evitare di dereferenziare l'indirizzo di un membro. Su sistemi con parole allineate mutibyte, ciò può comportare un'eccezione disallineata e un coredump.

Alcune persone si preoccuperanno anche delle prestazioni e sostengono che l'utilizzo di queste strutture impaccate rallenterà il sistema. È vero che, dietro le quinte, il compilatore aggiunge codice per accedere ai membri di dati non allineati. Puoi vederlo guardando il codice assembly nel tuo IDE.

Ma poiché le strutture impaccate sono più utili per la comunicazione e l'archiviazione dei dati, i dati possono essere estratti in una rappresentazione non impaccata quando si lavora con essa in memoria. Normalmente non abbiamo bisogno di lavorare con l'intero pacchetto di dati in memoria comunque.

Ecco alcune discussioni rilevanti:

pragma pack (1) né __attribute__ ((allineato (1))) funziona

Il pacchetto __attribute __ ((pacchetto)) / #pragma di gcc non è sicuro?

http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html


6

C'è poco. Dal punto di vista della portabilità, charnon può essere inferiore a 8 bit e nulla può essere inferiore a char, quindi se una determinata implementazione C ha un tipo intero a 8 bit senza segno, lo sarà char. In alternativa, potrebbe non averne affatto uno, a quel punto tutti i typedeftrucchi sono discutibili.

Potrebbe essere usato per documentare meglio il tuo codice, nel senso che è chiaro che hai bisogno di byte a 8 bit lì e nient'altro. Ma in pratica è già una ragionevole aspettativa praticamente ovunque (ci sono piattaforme DSP su cui non è vero, ma le possibilità che il tuo codice sia in esecuzione sono scarse e potresti anche sbagliare usando un'asserzione statica nella parte superiore del tuo programma su tale piattaforma).


7
@Skizz - No, lo standard richiede unsigned chardi essere in grado di contenere valori compresi tra 0 e 255. Se riesci a farlo in 4 bit, il mio cappello è spento.
Chris Lutz,

1
"sarebbe un po 'più ingombrante" - ingombrante nel senso che dovresti camminare (nuotare, prendere un aereo, ecc.) fino a dove si trovava lo scrittore del compilatore, schiaffeggiarlo nella parte posteriore della testa e farli aggiungere uint8_tall'implementazione. Mi chiedo, i compilatori per DSP con caratteri a 16 bit in genere implementano uint8_to no?
Steve Jessop,

6
A proposito, ripensandoci, è forse il modo più semplice per dire "Ho davvero bisogno di 8 bit" - #include <stdint.h>e usare uint8_t. Se la piattaforma ce l'ha, te lo darà. Se la piattaforma non lo possiede, il programma non verrà compilato e il motivo sarà chiaro e chiaro.
Pavel Minaev,

2
Ancora nessun sigaro, scusa: "Per tipi interi senza segno diversi dal carattere senza segno, i bit della rappresentazione dell'oggetto devono essere divisi in due gruppi: bit di valore e bit di riempimento ... Se ci sono N bit di valore, ogni bit deve rappresentare un diverso potenza di 2 tra 1 e 2 ^ (N-1), in modo che gli oggetti di quel tipo siano in grado di rappresentare valori da 0 a 2 ^ (N-1) usando una rappresentazione binaria pura ... Il nome tipografico intN_t designa un tipo intero con segno con larghezza N, nessun bit di riempimento e rappresentazione del complemento a due ".
Pavel Minaev,

1
Se hai solo bisogno di un modulo aritmetico, il bitfield senza segno andrà bene (se scomodo). È quando hai bisogno, diciamo, di una serie di ottetti senza imbottitura, ecco quando sei SOL. La morale della storia non è codificare i DSP e attenersi a architetture di caratteri a 8 bit corrette e oneste a Dio :)
Pavel Minaev,

4

Questo è molto importante, ad esempio, quando si scrive un analizzatore di rete. le intestazioni dei pacchetti sono definite dalle specifiche del protocollo, non dal modo in cui il compilatore C di una particolare piattaforma funziona.


quando ho chiesto questo ero definint un semplice protocollo per la comunicazione seriale.
Lyndon White,

2

Su quasi tutti i sistemi che ho incontrato uint8_t == char senza segno, ma questo non è garantito dallo standard C. Se stai cercando di scrivere un codice portatile e importa esattamente quale dimensione è la memoria, usa uint8_t. Altrimenti usa caratteri non firmati.


3
uint8_t corrisponde sempre all'intervallo, alle dimensioni unsigned chare al riempimento (nessuno) quando unsigned char è a 8 bit. Quando unsigned charnon è a 8 bit, uint8_tnon esiste.
chux - Ripristina Monica il

@chux, hai un riferimento al posto esatto nello standard in cui lo dice? Se unsigned charè 8-bit, viene uint8_tgarantita una typedefloro e non typedefdi un tipo intero senza segno esteso ?
hsivonen,

@hsivonen "posto esatto nello standard in cui si dice che?" -> No - ma guarda a 7.20.1.1. Viene facilmente dedotto come unsigned char/signed char/charil tipo più piccolo, non inferiore a 8 bit. unsigned charnon ha imbottitura. Per uint8_tessere, deve essere 8 bit, nessun padding, a causa di un'implementazione fornita di tipo intero: corrispondente ai requisiti minimi di unsigned char. Quanto a "... garantito di essere un typedef ..." sembra una buona domanda da pubblicare.
chux - Ripristina Monica il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.