Che cos'è un carattere non firmato?


479

In C / C ++, a cosa unsigned charserve un? In che cosa differisce da un normale char?

Risposte:


548

In C ++, ci sono tre tipi di caratteri distinti :

  • char
  • signed char
  • unsigned char

Se si utilizzano tipi di caratteri per il testo , utilizzare la non qualificata char:

  • è il tipo di caratteri letterali come 'a'o '0'.
  • è il tipo che compone come stringhe C. "abcde"

Funziona anche come valore numerico, ma non è specificato se quel valore sia trattato come firmato o non firmato. Fai attenzione ai confronti dei personaggi attraverso le disuguaglianze, anche se se ti limiti ad ASCII (0-127) sei quasi al sicuro.

Se si utilizzano tipi di carattere come numeri , utilizzare:

  • signed char, che fornisce almeno l'intervallo da -127 a 127. (Da -128 a 127 è comune)
  • unsigned char, che fornisce almeno l'intervallo da 0 a 255.

"Almeno", perché lo standard C ++ fornisce solo l'intervallo minimo di valori che ogni tipo numerico deve coprire. sizeof (char)è necessario che sia 1 (ovvero un byte), ma in teoria un byte potrebbe essere ad esempio 32 bit. sizeofsarebbe ancora segnalare la sua dimensione come1 - il che significa che si potrebbe avere sizeof (char) == sizeof (long) == 1.


4
Per essere chiari, potresti avere caratteri a 32 bit e numeri interi a 32 bit e avere sizeof (int)! = Sizeof (char)? So che lo standard dice sizeof (char) == 1, ma la dimensione relativa di (int) si basa sulla differenza di dimensioni effettiva o sulla differenza di intervallo?
Joseph Garvin,

14
+1. Ma ci sono quattro tipi di caratteri distinti in C ++, wchar_t è uno di questi.
Eric Z,

11
da c ++ 11 hai 6 tipi distinti: char, char firmato, char unsigned, wchar_t, char16_t, char32_t.
marcinj,

12
@unheilig È comune posizionare uno spazio dopo sizeofperché non è una funzione ma un operatore. È uno stile ancora migliore omettere la parentesi quando si prende la dimensione di una variabile. sizeof *po sizeof (int). Questo chiarisce rapidamente se si applica a un tipo o una variabile. Allo stesso modo, è anche ridondante mettere la parentesi dopo return. Non è una funzione.
Patrick Schlüter,

3
" char: è il tipo di caratteri letterali come 'a'o '0'." è vero in C ++ ma non in C. In C, 'a'è un int.
chux - Ripristina Monica

92

Ciò dipende dall'implementazione, poiché lo standard C NON definisce la firma di char. A seconda della piattaforma, char può essere signedo unsigned, quindi è necessario richiedere esplicitamente signed charo unsigned charse l'implementazione dipende da esso. Utilizzalo solo charse intendi rappresentare i caratteri delle stringhe, poiché corrisponderanno a ciò che la tua piattaforma inserisce nella stringa.

La differenza tra signed chare unsigned charè come ti aspetteresti. Sulla maggior parte delle piattaforme, signed charsarà un numero di complemento di due bit a 8 bit che va da -128a 127e unsigned charsarà un numero intero senza segno a 8 bit ( 0a 255). Si noti che lo standard NON richiede che i chartipi abbiano 8 bit, solo quello sizeof(char)restituito 1. È possibile ottenere il numero di bit in un carattere con CHAR_BITin limits.h. Ci sono poche o nessuna piattaforma oggi dove questo sarà qualcosa di diverso 8, però.

C'è un bel riassunto di questo problema qui .

Come altri hanno già detto da quando l'ho pubblicato, stai meglio usando int8_te uint8_tse vuoi davvero rappresentare piccoli numeri interi.


2
char firmato hanno solo una serie minima di -127 a 127, non da -128 a 127
12431234123412341234123

3
@ 12431234123412341234123: tecnicamente vero, in quanto lo standard C definisce da -127 a 127 l'intervallo minimo. Tuttavia, ti sfido a trovare una piattaforma che non usi l'aritmetica del complemento di due. Su quasi tutte le piattaforme moderne, la gamma effettiva di caratteri firmati sarà compresa tra -128 e 127.
Todd Gamblin,

CHAR_BITlo standard deve contenere almeno 8 bit.
martinkunev,

39

Perché penso che sia davvero richiesto, voglio solo dichiarare alcune regole di C e C ++ (sono le stesse in questo senso). In primo luogo, tutti i bit di unsigned charpartecipare nella determinazione del valore se qualsiasi oggetto char senza segno. In secondo luogo, unsigned charviene esplicitamente dichiarato non firmato.

Ora, ho avuto una discussione con qualcuno su cosa succede quando converti il ​​valore -1di type int in unsigned char. Ha rifiutato l'idea che il risultato unsigned charabbia tutti i suoi bit impostati su 1, perché era preoccupato per la rappresentazione dei segni. Ma non deve. È immediatamente seguito a questa regola che la conversione fa ciò che si intende:

Se il nuovo tipo non è firmato, il valore viene convertito aggiungendo o sottraendo ripetutamente uno in più rispetto al valore massimo che può essere rappresentato nel nuovo tipo fino a quando il valore non rientra nell'intervallo del nuovo tipo. ( 6.3.1.3p2in una bozza C99)

Questa è una descrizione matematica. C ++ lo descrive in termini di modulo modulo, che cede alla stessa regola. Comunque, ciò che non è garantito è che tutti i bit nell'intero -1sono uno prima della conversione. Quindi, cosa abbiamo in modo da poter affermare che il risultato unsigned charha tutti i suoi CHAR_BITbit trasformati in 1?

  1. Tutti i bit partecipano alla determinazione del suo valore, ovvero non si verificano bit di riempimento nell'oggetto.
  2. Aggiungendo una sola volta UCHAR_MAX+1a si -1otterrà un valore nell'intervallo, vale a direUCHAR_MAX

Questo è abbastanza, in realtà! Quindi ogni volta che vuoi avere uno unsigned charcon tutti i suoi bit, lo fai

unsigned char c = (unsigned char)-1;

Ne consegue anche che una conversione non sta semplicemente troncando bit di ordine superiore. Il fortunato evento per il complemento a due è che lì è solo un troncamento, ma lo stesso non è necessariamente vero per altre rappresentazioni di segni.


2
Perché non usare solo UCHAR_MAX?
Nicolás,

1
Perché (unsigned type)-1è una specie di linguaggio. ~0non lo è.
Patrick Schlüter,

1
se ho qualcosa del genere int x = 1234e char *y = &x. Rappresentazione binaria di 1234 is 00000000 00000000 00000100 11010010. La mia macchina è un po 'endian, quindi la inverte e la memoria 11010010 00000100 00000000 00000000LSB viene prima di tutto. Ora parte principale. se io uso printf("%d" , *p). printfleggerà il primo byte 11010010solo l'output è -46ma 11010010è 210per questo che stampa -46. Sono davvero confuso, immagino che un po 'di promozione alla promozione dei numeri interi stia facendo qualcosa, ma non lo so.
Suraj Jain,

27

Come ad esempio gli usi di caratteri non firmati :

unsigned charviene spesso utilizzato nella computer grafica, che molto spesso (anche se non sempre) assegna un singolo byte a ciascun componente di colore. È comune vedere un colore RGB (o RGBA) rappresentato come 24 (o 32) bit, ciascuno un unsigned char. Poiché i unsigned charvalori rientrano nell'intervallo [0,255], i valori vengono generalmente interpretati come:

  • 0 indica una totale mancanza di un determinato componente di colore.
  • 255 che significa il 100% di un determinato pigmento di colore.

Quindi potresti finire con RGB rosso come (255,0,0) -> (100% rosso, 0% verde, 0% blu).

Perché non usare un signed char? L'aritmetica e lo spostamento dei bit diventano problematici. Come già spiegato, signed charl'intervallo di a è sostanzialmente spostato di -128. Un metodo molto semplice e ingenuo (per lo più inutilizzato) per convertire RGB in scala di grigi è la media di tutti e tre i componenti di colore, ma ciò si traduce in problemi quando i valori dei componenti di colore sono negativi. Il rosso (255, 0, 0) ha una media di (85, 85, 85) quando si usa l' unsigned chararitmetica. Tuttavia, se i valori fossero signed chars (127, -128, -128), finiremmo con (-99, -99, -99), che sarebbe (29, 29, 29) nel nostro unsigned charspazio, il che non è corretto .


13

Se si desidera utilizzare un carattere come un intero piccolo, il modo più sicuro per farlo è con i tipi int8_te uint8_t.


2
Non è una buona idea: int8_te uint8_tsono opzionali e non definiti su architetture in cui la dimensione dei byte non è esattamente di 8 bit. Viceversa, signed chare unsigned charsono sempre disponibili e in grado di conservare almeno 8 bit. Potrebbe essere un modo comune ma non il più sicuro .
Chqrlie,

2
Questo è un commento, non risponde alla domanda.
Lundin,

@chqrlie Quindi, vuoi dire, il modo più sicuro per rappresentare un piccolo numero intero, se vuoi risparmiare memoria, è di stare con signed chare unsigned char? O consiglieresti una migliore alternativa "più sicura" in quel caso particolare? Ad esempio, attenersi ai tipi interi "reali" signed inte unsigned intinvece per qualche motivo?
RobertS supporta Monica Cellio il

@ RobertS-ReinstateMonica: l'utilizzo di signed chare unsigned charè portatile per tutte le implementazioni conformi e consente di risparmiare spazio di archiviazione ma può causare un aumento delle dimensioni del codice. In alcuni casi, si risparmierebbe più spazio di archiviazione memorizzando piccoli valori in bitfield o singoli bit di tipi interi regolari. Non esiste una risposta assoluta a questa domanda, la pertinenza di questo approccio dipende dal caso specifico a portata di mano. E questa risposta non affronta comunque la domanda.
Chqrlie,

10

unsigned characcetta solo valori positivi .... come da 0 a 255

mentre

signed characcetta valori sia positivi che negativi .... come da -128 a +127


9

chare unsigned charnon è garantito che siano tipi a 8 bit su tutte le piattaforme: sono garantiti a 8 bit o più. Alcune piattaforme hanno byte a 9, 32 o 64 bit . Tuttavia, le piattaforme più comuni oggi (Windows, Mac, Linux x86, ecc.) Hanno byte a 8 bit.


8

signed char ha un intervallo da -128 a 127; unsigned charha un intervallo da 0 a 255.

char sarà equivalente al carattere con segno o al carattere senza segno, a seconda del compilatore, ma è un tipo distinto.

Se stai usando stringhe in stile C, usa semplicemente char. Se è necessario utilizzare i caratteri per l'aritmetica (piuttosto raro), specificare esplicitamente firmato o non firmato per la portabilità.


8

An unsigned charè un valore di byte senza segno (da 0 a 255). Potresti pensare charin termini di essere un "personaggio" ma è davvero un valore numerico. Il normale charè firmato, quindi hai 128 valori e questi valori sono associati a caratteri usando la codifica ASCII. Ma in entrambi i casi, ciò che stai memorizzando è un valore in byte.


7

In termini di valori diretti viene utilizzato un carattere normale quando si sa che i valori sono compresi tra CHAR_MINe CHAR_MAXmentre un carattere senza segno fornisce il doppio dell'intervallo sull'estremità positiva. Ad esempio, se CHAR_BITè 8, l'intervallo del regolare charè garantito solo su [0, 127] (perché può essere firmato o non firmato) mentre unsigned charsarà [0, 255] esigned char sarà [-127, 127].

In termini di utilizzo, gli standard consentono agli oggetti di POD (semplici vecchi dati) di essere convertiti direttamente in una matrice di caratteri non firmati. Ciò consente di esaminare la rappresentazione e gli schemi di bit dell'oggetto. La stessa garanzia di punzonatura di tipo sicuro non esiste per il carattere o il carattere firmato.


In realtà, sarà più spesso [-128, 128].
RastaJedi,

Gli standard definiscono solo formalmente rappresentazione dell'oggetto come sequenza di unsigned char, non una matrice specificamente, e qualsiasi "conversione" è solo formalmente definito dal copiando dall'oggetto reale, dichiarata matrice di unsigned chare quindi ispezionare quest'ultimo. Non è chiaro se l'OR possa essere reinterpretato direttamente come tale array, con le indennità per l'aritmetica del puntatore che ciò comporterebbe, ovvero se =="array" di "sequenza " in questo utilizzo. C'è un numero principale 1701 aperto nella speranza di ottenere questo chiarimento. Per fortuna, poiché questa ambiguità mi sta davvero infastidendo di recente.
underscore_d

1
@RastaJedi No, non lo farà. Non può. Un intervallo di -128 ... + 128 è fisicamente impossibile da rappresentare con 8 bit. Quella larghezza supporta solo 2 ^ 8 == 256 valori discreti, ma -128 ... + 128 = 2 * 128 + 1 per 0 = 257. La rappresentazione della magnitudine dei segni consente -127 ... + 127 ma ha 2 (bipolare) zeri. La rappresentazione del complemento a due mantiene un singolo zero ma costituisce l'intervallo avendo un valore in più sul lato negativo; consente -128 ... + 127. (E così via per entrambe con larghezze di bit maggiori.)
underscore_d

Per quanto riguarda il mio secondo commento, è ragionevole presumere che possiamo prendere un puntatore al 1 ° unsigned chardell'OR e quindi procedere utilizzando ++ptrda lì per leggere ogni byte di esso ... ma AFAICT, non è specificamente definito come consentito, quindi siamo lasciato a dedurre che è "probabilmente OK" da molti altri passaggi (e in molti modi, la semplice esistenza di memcpy) nello Standard, simile a un puzzle. Quale non è l'ideale. Bene, forse la formulazione migliorerà alla fine. Ecco il problema CWG che ho citato ma che mancava di spazio per il collegamento - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701
underscore_d

@underscore_d scusa, era un errore di battitura. [-128, 127] è ciò che intendevo digitare: p. Sì, conosco i doppi zeri (zero "positivo" e "negativo") con segno / magnitudine. Devo essere stato stanco: p.
RastaJedi,

5

unsigned charè il cuore di tutti i trucchi. In quasi TUTTI i compilatori per TUTTE le piattaforme un unsigned charè semplicemente un byte e un numero intero senza segno di (solitamente) 8 bit che può essere trattato come un intero piccolo o un pacchetto di bit.

Inoltre, come ha detto qualcun altro, lo standard non definisce il segno di un carattere. in modo da avere 3 distinte chartipologie: char, signed char, unsigned char.


1
Un po 'di inganno, noto anche come un gioco di parole o di hacking, è noto per causare dipendenza ;-)
chqrlie,

3
Sono gli 0 che causano problemi. Per evitare la dipendenza da twiddling, stai lontano dai pezzi cattivi.
DragonLord,

5

Se ti piace utilizzando vari tipi di specifica lunghezza e signedness, siete probabilmente meglio con uint8_t, int8_t, uint16_t, ecc semplicemente perché fanno esattamente quello che dicono.


4

Alcuni googling hanno trovato questo , dove le persone hanno discusso di questo.

Un carattere senza segno è sostanzialmente un singolo byte. Quindi, lo useresti se hai bisogno di un byte di dati (ad esempio, forse vuoi usarlo per attivare e disattivare i flag da passare a una funzione, come spesso accade nell'API di Windows).


4

Un carattere senza segno utilizza il bit riservato per il segno di un carattere normale come un altro numero. Ciò modifica l'intervallo su [0 - 255] anziché [-128 - 127].

I caratteri generalmente non firmati vengono utilizzati quando non si desidera un segno. Ciò farà la differenza quando si fanno cose come spostare i bit (shift estende il segno) e altre cose quando si ha a che fare con un carattere come byte anziché usarlo come numero.


4

unsigned characcetta solo valori positivi: da 0 a 255 mentre signed characcetta valori positivi e negativi: da -128 a +127.


3

citato dal libro "the laugage di programmazione c":

Il qualificatore signedo unsignedpuò essere applicato a char o a qualsiasi numero intero. i numeri senza segno sono sempre positivi o zero e obbediscono alle leggi dell'aritmetica modulo 2 ^ n, dove n è il numero di bit nel tipo. Quindi, ad esempio, se i caratteri sono 8 bit, le variabili dei caratteri senza segno hanno valori compresi tra 0 e 255, mentre i caratteri con segno hanno valori compresi tra -128 e 127 (in una macchina a complemento a due.) Se i caratteri semplici sono firmati o non firmati è macchina -dipendenti, ma i caratteri stampabili sono sempre positivi.


2

signed chared unsigned charentrambi rappresentano 1 byte, ma hanno intervalli diversi.

   Type        |      range
-------------------------------
signed char    |  -128 to +127
unsigned char  |     0 to 255

In signed charse consideriamo char letter = 'A', 'A' è rappresentare binaria di 65 ASCII/Unicode, se 65 possono essere immagazzinate, -65 anche possono essere memorizzati. Non ci sono valori binari negativi inASCII/Unicode lì per non preoccuparsi di valori negativi.

Esempio

#include <stdio.h>

int main()
{
    signed char char1 = 255;
    signed char char2 = -128;
    unsigned char char3 = 255;
    unsigned char char4 = -128;

    printf("Signed char(255) : %d\n",char1);
    printf("Unsigned char(255) : %d\n",char3);

    printf("\nSigned char(-128) : %d\n",char2);
    printf("Unsigned char(-128) : %d\n",char4);

    return 0;
}

Produzione -:

Signed char(255) : -1
Unsigned char(255) : 255

Signed char(-128) : -128
Unsigned char(-128) : 128
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.