Domanda

Dato che gli ints con segno e senza segno usano gli stessi registri, ecc. e interpretano semplicemente i pattern di bit in modo diverso e che i caratteri C sono fondamentalmente solo in 8 bit, qual è la differenza tra caratteri con o senza segno in C? Capisco che la firma di char è definita dall'implementazione e semplicemente non riesco a capire come possa mai fare la differenza, almeno quando char viene usato per contenere stringhe invece di fare matematica.

È stato utile?

Soluzione

Non farà differenza per le stringhe. Ma in C puoi usare un carattere per fare matematica, quando farà la differenza.

In effetti, quando si lavora in ambienti con memoria limitata, come le applicazioni integrate a 8 bit, un carattere verrà spesso usato per fare matematica, e quindi fa una grande differenza. Questo perché non esiste un byte tipo di default in C.

Altri suggerimenti

In termini di valori che rappresentano:

carattere non firmato:

  • si estende nell'intervallo di valori 0..255 (00000000..11111111)
  • i valori traboccano attorno al bordo inferiore come:

    0 - 1 = 255 (00000000 - 00000001 = 11111111)

  • i valori traboccano attorno al bordo superiore come:

    255 + 1 = 0 (11111111 + 00000001 = 00000000)

  • l'operatore di spostamento a destra bit a bit (>>) esegue uno spostamento logico:

    10000000 >> 1 = 01000000 (128 / 2 = 64)

carattere firmato:

  • si estende nell'intervallo di valori -128..127 (10000000..01111111)
  • i valori traboccano attorno al bordo inferiore come:

    -128 - 1 = 127 (10000000 - 00000001 = 01111111)

  • i valori traboccano attorno al bordo superiore come:

    127 + 1 = -128 (01111111 + 00000001 = 10000000)

  • l'operatore di spostamento a destra bit a bit (10000000 >> 1 = 11000000 (-128 / 2 = -64)) esegue uno spostamento aritmetico:

    <=>

Ho incluso le rappresentazioni binarie per mostrare che il comportamento di wrapping del valore è puro, coerente aritmetica binaria e non ha nulla a che fare con un carattere che viene firmato / non firmato (aspettarsi che i turni giusti).

Aggiorna

Alcuni comportamenti specifici dell'implementazione menzionati nei commenti:

#include <stdio.h>

int main(int argc, char** argv)
{
    char a = 'A';
    char b = 0xFF;
    signed char sa = 'A';
    signed char sb = 0xFF;
    unsigned char ua = 'A';
    unsigned char ub = 0xFF;
    printf("a > b: %s\n", a > b ? "true" : "false");
    printf("sa > sb: %s\n", sa > sb ? "true" : "false");
    printf("ua > ub: %s\n", ua > ub ? "true" : "false");
    return 0;
}


[root]# ./a.out
a > b: true
sa > sb: true
ua > ub: false

È importante quando si ordinano le stringhe.

Ci sono un paio di differenze. Soprattutto, se si trabocca l'intervallo valido di un carattere assegnandogli un numero intero troppo grande o piccolo e il carattere è firmato, il valore risultante è definito dall'implementazione o anche un segnale (in C) potrebbe essere aumentato, come per tutti i tipi firmati . Contrastalo al caso quando assegni qualcosa di troppo grande o piccolo a un carattere senza segno: il valore si avvolge e otterrai una semantica definita con precisione. Ad esempio, assegnando un -1 a un carattere senza segno, otterrai un UCHAR_MAX. Quindi ogni volta che hai un byte come in un numero compreso tra 0 e 2 ^ CHAR_BIT, dovresti davvero usare un carattere senza segno per memorizzarlo.

Il segno fa anche la differenza quando si passa alle funzioni vararg:

char c = getSomeCharacter(); // returns 0..255
printf("%d\n", c);

Supponiamo che il valore assegnato a c sia troppo grande per essere rappresentato da char, e la macchina usa il complemento a due. Molte implementazioni si comportano nel caso in cui si assegni un valore troppo grande al carattere, in quanto il modello di bit non cambierà. Se un int sarà in grado di rappresentare tutti i valori di char (che è per la maggior parte delle implementazioni), allora il char viene promosso a int prima di passare a printf. Quindi, il valore di ciò che viene passato sarebbe negativo. La promozione a int manterrà quel segno. Quindi otterrai un risultato negativo. Tuttavia, se char è senza segno, allora il valore è senza segno e la promozione a un int produrrà un int positivo. Puoi usare il carattere senza segno, quindi otterrai un comportamento definito con precisione sia per l'assegnazione alla variabile, sia passando a printf che stamperà qualcosa di positivo.

Nota che un carattere char, non firmato e firmato ha tutti almeno una larghezza di 8 bit. Non è necessario che char sia esattamente largo 8 bit. Tuttavia, per la maggior parte dei sistemi è vero, ma per alcuni troverete che usano caratteri a 32 bit. Un byte in C e C ++ è definito per avere la dimensione di char, quindi anche un byte in C non è sempre esattamente 8 bit.

Un'altra differenza è che in C un carattere senza segno non deve avere bit di riempimento. Cioè, se trovi CHAR_BIT uguale a 8, i valori di un carattere senza segno devono variare da 0 .. 2 ^ CHAR_BIT-1. Lo stesso vale per char se non è firmato. Per il carattere con segno, non puoi assumere nulla sull'intervallo di valori, anche se sai come il tuo compilatore implementa il materiale dei segni (complemento a due o altre opzioni), potrebbero esserci dei bit di riempimento inutilizzati. In C ++, non ci sono bit di riempimento per tutti e tre i tipi di caratteri.

  
    

" Cosa significa firmare un carattere? "

  

Tradizionalmente, il set di caratteri ASCII è costituito da codifiche di caratteri a 7 bit. (A differenza di EBCIDIC a 8 bit.)

Quando il linguaggio C è stato progettato e implementato, questo era un problema significativo. (Per vari motivi come la trasmissione di dati su dispositivi modem seriali.) Il bit in più ha la stessa parità.

A " carattere firmato " sembra essere perfetto per questa rappresentazione.

I dati binari, OTOH, stanno semplicemente prendendo il valore di ogni " a 8 bit; chunk " dei dati, quindi non è necessario alcun segno.

L'aritmetica sui byte è importante per la computer grafica (dove i valori a 8 bit vengono spesso utilizzati per memorizzare i colori). A parte questo, posso pensare a due casi principali in cui il segno del carattere è importante:

  • conversione in un int più grande
  • funzioni di confronto

La cosa cattiva è che non ti morderanno se tutti i dati della stringa sono a 7 bit. Tuttavia, promette di essere una fonte infinita di bug oscuri se stai cercando di rendere pulito il tuo programma C / C ++ a 8 bit.

Signedness funziona più o meno allo stesso modo in char s come in altri tipi integrali. Come hai notato, i caratteri sono in realtà solo numeri interi a un byte. ( Non necessariamente a 8 bit , però! C'è una differenza; un byte potrebbe essere più grande di 8 bit su alcune piattaforme e sizeof(char) s sono piuttosto legati ai byte a causa delle definizioni di CHAR_BIT e <limits.h>. La <climits> macro, definita in byte o in C ++ (u?)int_least8_t, ti dirà quanti bit ci sono in <stdint.h>.).

Per quanto riguarda il motivo per cui vorresti un personaggio con un segno: in C e C ++, non esiste un tipo standard chiamato <cstdint>. Per il compilatore, <=> s sono byte e viceversa e non li distingue. A volte, però, vuoi - a volte vuoi che <=> sia un numero a un byte, e in quei casi (in particolare quanto piccolo può avere un intervallo), in genere importa se il numero è firmato o no. Ho usato personalmente il segno (o il segno) per dire che un certo <=> è un (numerico) & Quot; byte & Quot; piuttosto che un personaggio, e che verrà usato numericamente. Senza una firma specificata, <=> è davvero un carattere ed è destinato a essere usato come testo.

Lo facevo piuttosto. Ora le versioni più recenti di C e C ++ hanno <=> (attualmente digitate in <=> o <=>), che sono più esplicitamente numeriche (anche se in genere saranno solo typedef per tipi <=> firmati e non firmati ).

L'unica situazione che posso immaginare sia un problema se scegli di fare matematica sui caratteri. È perfettamente legale scrivere il seguente codice.

char a = (char)42;
char b = (char)120;
char c = a + b;

A seconda della firma del carattere, c potrebbe essere uno dei due valori. Se i caratteri non sono firmati, allora c sarà (carattere) 162. Se sono firmati, si verificherà un caso di overflow poiché il valore massimo per un carattere firmato è 128. Suppongo che la maggior parte delle implementazioni restituirebbe solo (carattere) -32.

Una cosa sui caratteri firmati è che puoi provare c > = '' (spazio) ed essere sicuro che sia un normale carattere ASCII stampabile. Certo, non è portatile, quindi non molto utile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top