Was ist ein Zeichen ohne Vorzeichen?


479

Wofür unsigned charwird in C / C ++ ein verwendet? Wie unterscheidet es sich von einem normalen char?

Antworten:


548

In C ++ gibt es drei verschiedene Zeichentypen:

  • char
  • signed char
  • unsigned char

Wenn Sie Zeichentypen für Text verwenden , verwenden Sie Folgendes char:

  • Es ist die Art von Zeichenliteralen wie 'a'oder '0'.
  • Es ist der Typ, aus dem C-Strings bestehen "abcde"

Es funktioniert auch als Zahlenwert, es ist jedoch nicht angegeben, ob dieser Wert als signiert oder nicht signiert behandelt wird. Hüten Sie sich vor Charaktervergleichen durch Ungleichungen - obwohl Sie, wenn Sie sich auf ASCII (0-127) beschränken, fast sicher sind.

Wenn Sie Zeichentypen als Zahlen verwenden , verwenden Sie:

  • signed char, was Ihnen mindestens den Bereich von -127 bis 127 gibt. (-128 bis 127 ist üblich)
  • unsigned char, was Ihnen mindestens den Bereich von 0 bis 255 gibt.

"Zumindest", da der C ++ - Standard nur den Mindestwertebereich angibt, den jeder numerische Typ abdecken muss. sizeof (char)muss 1 sein (dh ein Byte), aber ein Byte könnte theoretisch beispielsweise 32 Bit sein. sizeofwäre noch seine Größe berichten wie1 - was bedeutet , dass Sie könnte haben sizeof (char) == sizeof (long) == 1.


4
Könnten Sie 32-Bit-Zeichen und 32-Bit-Ganzzahlen und sizeof (int)! = Sizeof (char) haben? Ich weiß, dass der Standard sizeof (char) == 1 sagt, aber basiert die relative Größe von (int) auf dem tatsächlichen Größenunterschied oder dem Unterschied im Bereich?
Joseph Garvin

14
+1. In C ++ gibt es jedoch vier verschiedene Zeichentypen, wchar_t ist einer davon.
Eric Z

11
seit c ++ 11 gibt es 6 verschiedene typen: char, signiertes char, nicht signiertes char, wchar_t, char16_t, char32_t.
März

12
@unheilig Es ist üblich, ein Leerzeichen danach zu setzen, sizeofda es sich nicht um eine Funktion, sondern um einen Operator handelt. Es ist imho noch besser, die Klammer wegzulassen, wenn man die Größe einer Variablen nimmt. sizeof *poder sizeof (int). Dies macht schnell klar, ob es sich um einen Typ oder eine Variable handelt. Ebenso ist es überflüssig, nach Klammern zu setzen return. Es ist keine Funktion.
Patrick Schlüter

3
" char: Es ist die Art von Zeichenliteralen wie 'a'oder '0'." ist wahr in C ++ aber nicht C. In C 'a'ist ein int.
chux

92

Dies ist implementierungsabhängig, da der C-Standard NICHT die Signatur von definiert char. Je nach Plattform kann char signedoder sein unsigned, daher müssen Sie explizit nachfragen signed charoder unsigned charob Ihre Implementierung davon abhängt. Verwenden charSie diese Option nur, wenn Sie Zeichen aus Zeichenfolgen darstellen möchten, da dies mit dem übereinstimmt, was Ihre Plattform in die Zeichenfolge einfügt.

Der Unterschied zwischen signed charund unsigned charist wie erwartet. Auf den meisten Plattformen signed charwird eine 8-Bit-Zweierkomplementzahl von reichen-128 bis 127und unsigned chareine 8-Bit-Ganzzahl ohne Vorzeichen ( 0bis 255). Beachten Sie, dass der Standard NICHT verlangt, dass charTypen 8 Bit haben, sondern nur diese sizeof(char)Rückgabe 1. Sie können die Anzahl der Bits in einem Zeichen mit CHAR_BITin ermitteln limits.h. Es gibt heutzutage nur wenige Plattformen, auf denen dies etwas anderes sein 8wird.

Es gibt eine schöne Zusammenfassung dieses Problem hier .

Wie andere erwähnt haben , da ich dies geschrieben, du bist besser dran mit int8_tund uint8_twenn Sie wirklich kleine ganze Zahlen darstellen wollen.


2
signierte Zeichen haben nur einen Mindestbereich von -127 bis 127, nicht von -128 bis 127
12431234123412341234123

3
@ 12431234123412341234123: Technisch gesehen, da der C-Standard -127 bis 127 als Mindestbereich definiert. Ich fordere Sie jedoch auf, eine Plattform zu finden, die keine Zweierkomplementarithmetik verwendet. Auf fast jeder modernen Plattform wird der tatsächliche Bereich der signierten Zeichen -128 bis 127 sein.
Todd Gamblin

CHAR_BITDer Standard muss mindestens 8 Bit betragen.
Martinkunev

39

Da ich der Meinung bin, dass dies wirklich erforderlich ist, möchte ich nur einige Regeln für C und C ++ angeben (sie sind in dieser Hinsicht gleich). Zuerst werden alle Bits der unsigned charTeilnahme den Wert , wenn jeder unsigned char Objekt zu bestimmen. Zweitens unsigned charwird ausdrücklich ohne Vorzeichen angegeben.

Jetzt hatte ich eine Diskussion mit jemandem darüber, was passiert, wenn Sie den Wert -1vom Typ int in konvertieren unsigned char. Er lehnte die Idee ab, dass das Ergebnis unsigned charalle seine Bits auf 1 gesetzt hat, weil er sich Sorgen um die Darstellung der Zeichen machte. Aber das muss er nicht. Aus dieser Regel folgt unmittelbar, dass die Konvertierung das tut, was beabsichtigt ist:

Wenn der neue Typ nicht signiert ist, wird der Wert konvertiert, indem wiederholt mehr als der Maximalwert addiert oder subtrahiert wird, der im neuen Typ dargestellt werden kann, bis der Wert im Bereich des neuen Typs liegt. (6.3.1.3p2 in einem C99-Entwurf)

Das ist eine mathematische Beschreibung. C ++ beschreibt es mit Modulo-Kalkül, das der gleichen Regel entspricht. Was jedoch nicht garantiert ist, ist, dass alle Bits in der Ganzzahl -1vor der Konvertierung eins sind. Was haben wir also, damit wir behaupten können, dass das Ergebnis unsigned charalle seine CHAR_BITBits auf 1 gesetzt hat?

  1. Alle Bits sind an der Bestimmung ihres Werts beteiligt, dh es treten keine Auffüllbits im Objekt auf.
  2. Nur einmal hinzufügen UCHAR_MAX+1zu-1 erhalten Sie einen Wert im Bereich, nämlichUCHAR_MAX

Das reicht eigentlich! Also, wann immer Sie wollen, unsigned chardass alle seine Teile eins haben, tun Sie es

unsigned char c = (unsigned char)-1;

Daraus folgt auch, dass eine Konvertierung nicht nur Bits höherer Ordnung abschneidet. Das glückliche Ereignis für das Zweierkomplement ist, dass es dort nur eine Kürzung ist, aber dasselbe gilt nicht unbedingt für andere Zeichendarstellungen.


2
Warum nicht einfach benutzen UCHAR_MAX?
Nicolás

1
Weil (unsigned type)-1es eine Art Redewendung ist. ~0ist nicht.
Patrick Schlüter

1
wenn ich so etwas habe int x = 1234und char *y = &x. Binäre Darstellung von 1234 ist 00000000 00000000 00000100 11010010. Meine Maschine ist Little Endian, also kehrt sie es um und das Speichern im Speicher 11010010 00000100 00000000 00000000LSB steht an erster Stelle. Jetzt Hauptteil. wenn ich benutze printf("%d" , *p). printflesen werden erste Byte ist 11010010der Ausgang nur , -46sondern 11010010ist 210so , warum gedruckt wird es -46. Ich bin wirklich verwirrt, ich denke, ein Zeichen für eine ganzzahlige Werbung macht etwas, aber ich weiß es nicht.
Suraj Jain

27

Wie zum Beispiel die Verwendung von nicht signiertem Zeichen :

unsigned charwird häufig in Computergrafiken verwendet, die sehr oft (wenn auch nicht immer) jeder Farbkomponente ein einzelnes Byte zuweisen. Es ist üblich, eine RGB- (oder RGBA-) Farbe zu sehen, die als 24 (oder 32) Bits dargestellt wird unsigned char. Da die unsigned charWerte in den Bereich [0,255] fallen, werden die Werte normalerweise wie folgt interpretiert:

  • 0 bedeutet ein völliges Fehlen einer bestimmten Farbkomponente.
  • 255 bedeutet 100% eines bestimmten Farbpigments.

Sie würden also RGB-Rot als (255,0,0) -> (100% Rot, 0% Grün, 0% Blau) erhalten.

Warum nicht ein signed char? Arithmetik und Bitverschiebung werden problematisch. Wie bereits erläutert, signed charist der Bereich von a im Wesentlichen um -128 verschoben. Eine sehr einfache und naive (meist nicht verwendete) Methode zum Konvertieren von RGB in Graustufen besteht darin, alle drei Farbkomponenten zu mitteln. Dies ist jedoch problematisch, wenn die Werte der Farbkomponenten negativ sind. Rot (255, 0, 0) ergibt bei Verwendung der unsigned charArithmetik einen Durchschnittswert von (85, 85, 85) . Wenn die Werte jedoch signed chars (127, -128, -128) wären, würden wir am Ende (-99, -99, -99) haben, was (29, 29, 29) in unserem unsigned charRaum wäre, was falsch ist .


13

Wenn Sie ein Zeichen als kleine Ganzzahl verwenden möchten, ist es am sichersten, dies mit den Typen int8_tund zu tun uint8_t.


2
Keine gute Idee: int8_tund uint8_tsind optional und nicht auf Architekturen definiert, bei denen die Bytegröße nicht genau 8 Bit beträgt. Umgekehrt signed charund unsigned charsind immer verfügbar und halten garantiert mindestens 8 Bit. Es mag ein üblicher Weg sein, aber nicht der sicherste .
Chqrlie

2
Dies ist ein Kommentar, der die Frage nicht beantwortet.
Lundin

@chqrlie Du meinst also, der wirklich sicherste Weg, eine kleine Ganzzahl darzustellen, wenn du Speicher sparen willst, ist, mit signed charund zu bleiben unsigned char? Oder würden Sie in diesem speziellen Fall eine bessere "sicherere" Alternative empfehlen? Zum Beispiel, um bei den "echten" Ganzzahltypen zu bleiben signed intund unsigned intstattdessen aus irgendeinem Grund?
RobertS unterstützt Monica Cellio

@ RobertS-ReinstateMonica: Die Verwendung von signed charund unsigned charist für alle konformen Implementierungen portierbar und spart Speicherplatz, kann jedoch zu einer gewissen Erhöhung der Codegröße führen. In einigen Fällen würde man mehr Speicherplatz sparen, indem kleine Werte in Bitfeldern oder einzelnen Bits regulärer Ganzzahltypen gespeichert werden. Es gibt keine absolute Antwort auf diese Frage, die Relevanz dieses Ansatzes hängt vom jeweiligen Einzelfall ab. Und diese Antwort spricht die Frage sowieso nicht an.
Chqrlie

10

unsigned charnimmt nur positive Werte an .... wie 0 bis 255

wohingegen

signed charnimmt sowohl positive als auch negative Werte an .... wie -128 bis +127


9

char und unsigned char es wird nicht garantiert, dass es sich auf allen Plattformen um 8-Bit-Typen handelt - es wird garantiert, dass sie 8-Bit oder größer sind. Einige Plattformen verfügen über 9-Bit-, 32-Bit- oder 64-Bit-Bytes . Die heute gängigsten Plattformen (Windows, Mac, Linux x86 usw.) haben jedoch 8-Bit-Bytes.


8

signed char hat einen Bereich von -128 bis 127; unsigned charhat einen Bereich von 0 bis 255.

char wird je nach Compiler entweder einem signierten oder einem nicht signierten Zeichen entsprechen, ist jedoch ein eindeutiger Typ.

Wenn Sie Zeichenfolgen im C-Stil verwenden, verwenden Sie einfach char. Wenn Sie Zeichen für die Arithmetik verwenden müssen (ziemlich selten), geben Sie für die Portabilität explizit signiert oder nicht signiert an.


8

An unsigned charist ein vorzeichenloser Bytewert (0 bis 255). Sie denken vielleicht daran char, ein "Zeichen" zu sein, aber es ist wirklich ein numerischer Wert. Der reguläre charWert ist signiert, sodass Sie 128 Werte haben. Diese Werte werden Zeichen mit ASCII-Codierung zugeordnet. In beiden Fällen ist das, was Sie im Speicher speichern, ein Bytewert.


7

In Bezug auf direkte Werte wird ein reguläres Zeichen verwendet, wenn bekannt ist, dass die Werte zwischen CHAR_MINund CHAR_MAXwährend ein vorzeichenloses Zeichen den doppelten Bereich am positiven Ende liefert. Wenn beispielsweise CHAR_BIT8 ist, charwird garantiert, dass der reguläre Bereich nur [0, 127] beträgt (da er signiert oder nicht signiert sein kann), während unsigned charer [0, 255] und signed char[-127, 127] ist.

In Bezug auf das, wofür es verwendet wird, ermöglichen die Standards, dass Objekte von POD (einfache alte Daten) direkt in ein Array von Zeichen ohne Vorzeichen konvertiert werden. Auf diese Weise können Sie die Darstellung und die Bitmuster des Objekts untersuchen. Die gleiche Garantie für sicheres Punnen besteht nicht für char oder signiertes char.


Tatsächlich wird es meistens [-128, 128] sein.
RastaJedi

Die Standards definieren die Objektdarstellung nur formal als eine Folge von unsigned char, nicht als spezifisches Array , und jede "Konvertierung" wird nur formal definiert, indem vom Objekt in ein reales, deklariertes Array von & kopiert wird und dieses dann überprüft wird. Es ist nicht klar, ob das ODER direkt als solches Array neu interpretiert werden kann, mit den zulässigen Zeigerarithmetiken, dh ob "Sequenz" "Array" in dieser Verwendung. Es gibt ein Kernproblem Nr. 1701, in der Hoffnung, dies zu klären. Zum Glück, denn diese Zweideutigkeit nervt mich in letzter Zeit wirklich. unsigned char==
underscore_d

1
@ RastaJedi Nein, das wird es nicht. Es kann nicht. Ein Bereich von -128 ... + 128 kann mit 8 Bit physikalisch nicht dargestellt werden. Diese Breite unterstützt nur 2 ^ 8 == 256 diskrete Werte, aber -128 ... + 128 = 2 * 128 + 1 für 0 = 257. Die Darstellung der Vorzeichengröße erlaubt -127 ... + 127, hat aber 2 (bipolar) Nullen. Die Zweierkomplementdarstellung behält eine einzelne Null bei, macht jedoch den Bereich aus, indem auf der negativen Seite ein weiterer Wert vorhanden ist. es erlaubt -128 ... + 127. (Und so weiter für beide bei größeren Bitbreiten.)
underscore_d

In Bezug auf meinen zweiten Kommentar ist es vernünftig anzunehmen, dass wir einen Zeiger auf den ersten unsigned chardes OP nehmen und dann ++ptrvon dort aus jedes Byte davon lesen können ... aber AFAICT, es ist nicht speziell als erlaubt definiert, also sind wir es links zu schließen, dass es "wahrscheinlich in Ordnung" aus vielen anderen Passagen (und in vielerlei Hinsicht der bloßen Existenz von memcpy) im Standard ist, ähnlich einem Puzzle. Welches ist nicht ideal. Nun, vielleicht wird sich der Wortlaut irgendwann verbessern. Hier ist das CWG-Problem, das ich erwähnt habe, aber es fehlte Platz zum Verknüpfen - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701
underscore_d

@underscore_d sorry, das war ein Tippfehler. [-128, 127] wollte ich eingeben: p. Ja, ich kenne die doppelten Nullen ('positive' und 'negative' Null) mit Vorzeichen / Größe. Ich muss müde gewesen sein: p.
RastaJedi

5

unsigned charist das Herz aller kleinen Tricks. In fast ALLEN Compilern für ALLE Plattformen unsigned charist ein einfach ein Byte und eine vorzeichenlose Ganzzahl von (normalerweise) 8 Bits, die als kleine Ganzzahl oder als Bitpaket behandelt werden können.

In der Sucht definiert der Standard, wie jemand anderes gesagt hat, nicht das Zeichen eines Zeichens. so haben Sie 3 verschiedene charArten: char, signed char, unsigned char.


1
Bit Trickery, auch bekannt als Bit Twiddling oder Bit Hacking, ist in der Tat dafür bekannt, Sucht zu verursachen
;-)

3
Es sind die Nullen, die Probleme verursachen. Halten Sie sich von den kleinen Dingen fern, um zu vermeiden, dass die Sucht herumwirbelt.
DragonLord

5

Wenn Sie verschiedene Arten von bestimmten Länge und Signedness wie verwendet, sind Sie wahrscheinlich besser dran mit uint8_t, int8_t, uint16_t, etc. , nur weil sie genau das tun , was sie sagen.


4

Einige Googler fanden dies , wo die Leute darüber diskutierten.

Ein vorzeichenloses Zeichen ist im Grunde ein einzelnes Byte. Sie würden dies also verwenden, wenn Sie ein Datenbyte benötigen (z. B. möchten Sie damit möglicherweise Flags aktivieren und deaktivieren, die an eine Funktion übergeben werden sollen, wie dies häufig in der Windows-API der Fall ist).


4

Ein vorzeichenloses Zeichen verwendet das Bit, das für das Vorzeichen eines regulären Zeichens reserviert ist, als andere Zahl. Dies ändert den Bereich auf [0 - 255] im Gegensatz zu [-128 - 127].

Im Allgemeinen werden vorzeichenlose Zeichen verwendet, wenn Sie kein Zeichen möchten. Dies macht einen Unterschied, wenn Sie beispielsweise Bits verschieben (Shift erweitert das Vorzeichen) und andere Dinge, wenn Sie ein Zeichen als Byte behandeln, anstatt es als Zahl zu verwenden.


4

unsigned charnimmt nur positive Werte an: 0 bis 255, während signed charpositive und negative Werte angenommen werden: -128 bis +127.


3

zitiert aus dem Buch "The C Programming Laugage":

Das Qualifikationsmerkmal signedoder unsignedkann auf char oder eine beliebige Ganzzahl angewendet werden. vorzeichenlose Zahlen sind immer positiv oder null und folgen den Gesetzen des arithmetischen Modulo 2 ^ n, wobei n die Anzahl der Bits im Typ ist. Wenn Zeichen beispielsweise 8 Bit sind, haben vorzeichenlose Zeichenvariablen Werte zwischen 0 und 255, während vorzeichenbehaftete Zeichen Werte zwischen -128 und 127 haben (in einer Zwei-Komplement-Maschine). Ob einfache Zeichen vorzeichenlos oder vorzeichenlos sind, ist Maschine -abhängige, aber druckbare Zeichen sind immer positiv.


2

signed charund unsigned charbeide repräsentieren 1 Byte, aber sie haben unterschiedliche Bereiche.

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

In signed charwenn man bedenkt , char letter = 'A'‚A‘ darstellen, binär von 65 in ASCII/Unicodekann Wenn 65 gespeichert werden , -65 auch gespeichert werden können. Es gibt keine negativen Binärwerte inASCII/Unicode sich keine Gedanken über negative Werte machen müssen.

Beispiel

#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;
}

Ausgabe -:

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

Signed char(-128) : -128
Unsigned char(-128) : 128
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.