W C / C ++ do czego unsigned char
służy an ? Czym różni się od zwykłego char
?
W C / C ++ do czego unsigned char
służy an ? Czym różni się od zwykłego char
?
Odpowiedzi:
W C ++ istnieją trzy różne typy znaków:
char
signed char
unsigned char
Jeśli używasz typów znaków dla tekstu , użyj niekwalifikowanego char
:
'a'
lub '0'
."abcde"
Działa również jako wartość liczbowa, ale nie jest określone, czy ta wartość jest traktowana jako podpisana czy niepodpisana. Uważaj na porównania postaci przez nierówności - chociaż ograniczając się do ASCII (0-127), jesteś prawie bezpieczny.
Jeśli używasz typów znaków jako liczb , użyj:
signed char
, co daje co najmniej zakres od -127 do 127. (Od -128 do 127 jest powszechne)unsigned char
, co daje co najmniej zakres od 0 do 255.„Przynajmniej”, ponieważ standard C ++ podaje tylko minimalny zakres wartości, który musi obejmować każdy typ liczbowy. sizeof (char)
wymagana jest wartość 1 (tj. jeden bajt), ale bajt teoretycznie może wynosić na przykład 32 bity. sizeof
nadal będzie zgłosić swoją wielkość, jak1
- co oznacza, że mogłyby mieć sizeof (char) == sizeof (long) == 1
.
sizeof
ponieważ nie jest to funkcja, ale operator. Jeszcze lepszym stylem jest pominięcie nawiasu przy przyjmowaniu wielkości zmiennej. sizeof *p
lub sizeof (int)
. Dzięki temu szybko staje się jasne, czy ma zastosowanie do typu lub zmiennej. Podobnie zbędne jest umieszczanie nawiasów po return
. To nie jest funkcja.
char
: to rodzaj literałów znakowych takich jak 'a'
lub '0'
.” jest prawdziwe w C ++, ale nie w C. W C 'a'
jest int
.
Jest to zależne od implementacji, ponieważ standard C NIE definiuje podpisu char
. W zależności od platformy char może być signed
lub unsigned
, więc musisz jawnie o to poprosić signed char
lub od unsigned char
tego zależy twoja implementacja. Po prostu użyjchar
jeśli zamierzasz reprezentować znaki z ciągów, ponieważ będą one pasować do tego, co twoja platforma umieszcza w ciągu.
Różnica między signed char
i unsigned char
jest taka, jak można się spodziewać. Na większości platform signed char
będzie 8-bitową liczbą uzupełnień do dwóch, od -128
do 127
, i unsigned char
będzie 8-bitową liczbą całkowitą bez znaku ( 0
do 255
). Uwaga standard nie wymaga, aby char
typy miały 8 bitów, tylko ten sizeof(char)
zwrot 1
. Możesz dostać się do liczby bitów w znaku z CHAR_BIT
in limits.h
. Istnieje jednak niewiele, jeśli w ogóle, platform, na których będzie to coś innego niż 8
.
Ładne streszczenie tego problemu znajduje się tutaj .
Jak wspomnieli inni, odkąd to opublikowałem, lepiej jest używać int8_t
i uint8_t
jeśli naprawdę chcesz reprezentować małe liczby całkowite.
CHAR_BIT
zgodnie z normą wymagane jest co najmniej 8 bitów.
Ponieważ czuję, że jest to naprawdę potrzebne, chcę tylko podać niektóre zasady C i C ++ (są one pod tym względem takie same). Po pierwsze, wszystkie bity od unsigned char
udziału w ustalaniu wartości, jeśli jakiekolwiek unsigned char obiektu. Po drugie, unsigned char
jest wyraźnie określony jako niepodpisany.
Teraz rozmawiałem z kimś o tym, co się dzieje, kiedy konwertujesz wartość -1
typu int na unsigned char
. Odrzucił ideę, że wynikowe unsigned char
ma wszystkie bity ustawione na 1, ponieważ martwił się reprezentacją znaków. Ale nie musi. Bezpośrednio z tej reguły wynika, że konwersja robi to, co jest zamierzone:
Jeśli nowy typ nie jest podpisany, wartość jest konwertowana przez wielokrotne dodawanie lub odejmowanie wartości większej niż maksymalna wartość, którą można przedstawić w nowym typie, dopóki wartość nie znajdzie się w zakresie nowego typu. (
6.3.1.3p2
w wersji roboczej C99)
To jest opis matematyczny. C ++ opisuje to w kategoriach rachunku modułowego, który daje tę samą regułę. W każdym razie, co nie gwarantuje, że wszystkie bity w całkowitej -1
są jednym przed konwersją. Co więc mamy, abyśmy mogli twierdzić, że wynikowy unsigned char
ma wszystkie CHAR_BIT
bity zmienione na 1?
UCHAR_MAX+1
na -1
przyniesie wartość w zakresie, a mianowicieUCHAR_MAX
Właściwie to wystarczy! Tak więc, kiedy tylko chcesz mieć unsigned char
wszystkie swoje bity, robisz to
unsigned char c = (unsigned char)-1;
Wynika z tego również, że konwersja to nie tylko obcinanie bitów wyższego rzędu. Szczęśliwym wydarzeniem dla uzupełnienia dwóch jest to, że jest to tylko obcięcie, ale niekoniecznie tak samo jest w przypadku innych reprezentacji znaków.
UCHAR_MAX
?
(unsigned type)-1
to jakiś idiom. ~0
nie jest.
int x = 1234
i char *y = &x
. Binarna reprezentacja 1234
jest 00000000 00000000 00000100 11010010
. Moja maszyna jest małym endianem, więc odwraca ją i zapisuje w pamięci 11010010 00000100 00000000 00000000
LSB na pierwszym miejscu. Teraz główna część. jeśli użyję printf("%d" , *p)
. printf
odczyta pierwszy bajt 11010010
tylko wyjście -46
, ale 11010010
jest 210
tak dlatego to wydrukować -46
. Naprawdę jestem zdezorientowany. Wydaje mi się, że jakiś znak do promocji liczb całkowitych robi coś, ale nie wiem.
Na przykład zastosowania niepodpisanego znaku :
unsigned char
jest często stosowany w grafice komputerowej, która bardzo często (choć nie zawsze) przypisuje jeden bajt do każdego komponentu koloru. Często zdarza się, że kolor RGB (lub RGBA) reprezentowany jest przez 24 (lub 32) bity każdy unsigned char
. Ponieważ unsigned char
wartości mieszczą się w zakresie [0,255], są one zazwyczaj interpretowane jako:
Tak więc otrzymałeś czerwony RGB jako (255,0,0) -> (100% czerwony, 0% zielony, 0% niebieski).
Dlaczego nie użyć signed char
? Arytmetyka i zmiana bitów staje się problematyczna. Jak już wyjaśniono, signed char
zakres a jest zasadniczo przesunięty o -128. Bardzo prostą i naiwną (najczęściej nieużywaną) metodą konwersji RGB na skalę szarości jest uśrednienie wszystkich trzech składników koloru, ale napotyka to problemy, gdy wartości składników koloru są ujemne. Średnie czerwone (255, 0, 0) to (85, 85, 85) przy zastosowaniu unsigned char
arytmetyki. Jednak jeśli wartości byłyby signed char
s (127, -128, -128), otrzymalibyśmy (-99, -99, -99), co byłoby (29, 29, 29) w naszej unsigned char
przestrzeni, co jest niepoprawne .
Jeśli chcesz użyć znaku jako małej liczby całkowitej, najbezpieczniejszym sposobem na to jest użycie typów int8_t
i uint8_t
.
int8_t
i uint8_t
są opcjonalne i nie zdefiniowano na architekturach gdzie wielkość bajt nie jest dokładnie 8 bitów. I odwrotnie, signed char
i unsigned char
zawsze są dostępne i gwarantują, że mieszczą co najmniej 8 bitów. Może to być powszechny sposób, ale nie najbezpieczniejszy .
signed char
i unsigned char
? A może poleciłbyś lepszą „bezpieczniejszą” alternatywę w tym konkretnym przypadku? Na przykład trzymać się „prawdziwych” liczb całkowitych signed int
i unsigned int
zamiast tego z jakiegoś powodu?
signed char
i unsigned char
jest przenośne dla wszystkich zgodnych implementacji i pozwoli zaoszczędzić miejsce, ale może spowodować pewne zwiększenie rozmiaru kodu. W niektórych przypadkach można zaoszczędzić więcej miejsca, przechowując małe wartości w polach bitowych lub pojedynczych bitach zwykłych liczb całkowitych. Nie ma absolutnej odpowiedzi na to pytanie, znaczenie tego podejścia zależy od konkretnego rozpatrywanego przypadku. Ta odpowiedź i tak nie odnosi się do pytania.
char
i unsigned char
nie ma gwarancji, że będą to typy 8-bitowe na wszystkich platformach - są gwarantowane, że są 8-bitowe lub większe. Niektóre platformy mają 9-bitowe, 32-bitowe lub 64-bitowe bajty . Jednak najpopularniejsze obecnie platformy (Windows, Mac, Linux x86 itp.) Mają 8-bitowe bajty.
signed char
ma zakres od -128 do 127; unsigned char
ma zakres od 0 do 255.
char
będzie równoważny znakowi podpisanemu lub znakowi niepodpisanemu, w zależności od kompilatora, ale jest odrębnym typem.
Jeśli używasz ciągów w stylu C, po prostu użyj char
. Jeśli musisz używać znaków do obliczeń arytmetycznych (dość rzadko), podaj wyraźnie podpisane lub niepodpisane w celu przenoszenia.
An unsigned char
jest bajtem bez znaku (od 0 do 255). Być może myślisz o char
byciu „postacią”, ale tak naprawdę jest to wartość liczbowa. Normalny char
jest podpisany, więc masz 128 wartości, które są mapowane na znaki przy użyciu kodowania ASCII. Ale w obu przypadkach to, co przechowujesz w pamięci, to wartość bajtowa.
Jeśli chodzi o wartości bezpośrednie, zwykły znak jest używany, gdy wiadomo, że wartości są pomiędzy, CHAR_MIN
a CHAR_MAX
gdy znak bez znaku zapewnia podwójny zakres na dodatnim końcu. Na przykład, jeśli CHAR_BIT
jest to 8, zakres wartości regularnych char
jest gwarantowany tylko na [0, 127] (ponieważ może być podpisany lub niepodpisany), podczas gdy unsigned char
będzie wynosił [0, 255] isigned char
będzie wynosił [-127, 127].
Pod względem tego, do czego jest używany, standardy pozwalają bezpośrednio konwertować obiekty POD (zwykłe stare dane) na tablicę znaków bez znaku. Umożliwia to sprawdzenie reprezentacji i wzorów bitowych obiektu. Ta sama gwarancja bezpiecznego znakowania czcionek nie istnieje dla znaków ani znaków podpisanych.
unsigned char
, a nie tablicy Specyficznie, każdy „konwersja” jest zdefiniowany tylko formalnie przez skopiowanie od obiektu do rzeczywistego, stwierdził tablicę z unsigned char
i następnie kontroli ostatnich. Nie jest jasne, czy OR można bezpośrednio zinterpretować jako taką tablicę, z uwzględnieniem dopuszczalnej arytmetyki wskaźnika, tj. Czy ==
„tablica” „tablica” w tym zastosowaniu. Mamy nadzieję, że uda się to wyjaśnić. Na szczęście, ponieważ ta dwuznaczność naprawdę mnie ostatnio denerwuje.
unsigned char
OR, a następnie kontynuować korzystanie ++ptr
z tego miejsca, aby odczytać każdy jego bajt ... ale AFAICT, nie jest specjalnie zdefiniowany jako dozwolony, więc jesteśmy pozostawiono, aby wywnioskować, że „prawdopodobnie jest OK” z wielu innych fragmentów (i na wiele sposobów, samego istnienia memcpy
) w standardzie, podobnie jak układanka. Co nie jest idealne. Cóż, być może brzmienie ostatecznie się poprawi. Oto problem CWG, o którym wspomniałem, ale brakowało miejsca na link - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701
unsigned char
jest sercem wszystkich sztuczek. W prawie WSZYSTKIM kompilatorze dla WSZYSTKICH platform jest unsigned char
to po prostu bajt i liczba całkowita bez znaku (zwykle) 8 bitów, które mogą być traktowane jako mała liczba całkowita lub paczka bitów.
Nałogowo, jak powiedział ktoś inny, standard nie definiuje znaku znaku. tak masz 3 różne char
typy: char
, signed char
, unsigned char
.
Jeśli podoba Ci się stosując różne typy i długości określonej signedness, jesteś prawdopodobnie lepiej z uint8_t
, int8_t
, uint16_t
itd po prostu dlatego, że robią dokładnie to, co mówią.
Niektórzy google znaleźli to , gdzie ludzie rozmawiali o tym.
Znak bez znaku jest w zasadzie pojedynczym bajtem. Tak więc użyłbyś tego, jeśli potrzebujesz jednego bajtu danych (na przykład, może chcesz go użyć do włączania i wyłączania flag, aby były przekazywane do funkcji, jak to często robi się w interfejsie API Windows).
Znak bez znaku używa bitu zarezerwowanego dla znaku zwykłego znaku jako innej liczby. Zmienia to zakres na [0–255] w przeciwieństwie do [-128–127].
Zasadniczo znaki bez znaku są używane, gdy nie chcesz znaku. Będzie to miało znaczenie podczas robienia rzeczy, takich jak przesuwanie bitów (shift wydłuża znak) i innych rzeczy, gdy ma się do czynienia z char jako bajtem, a nie z użyciem go jako liczby.
cytowany z książki „la cage programowania”:
Kwalifikator signed
lub unsigned
może być zastosowany do znaku lub dowolnej liczby całkowitej. liczby bez znaku są zawsze dodatnie lub zerowe i są zgodne z prawami arytmetycznego modułu 2 ^ n, gdzie n jest liczbą bitów w typie. Na przykład, jeśli znaki to 8 bitów, zmienne znakowane bez znaku mają wartości od 0 do 255, podczas gdy znaki podpisane mają wartości od -128 do 127 (w maszynie dopełniającej dwa.) To, czy zwykłe znaki są podpisane czy niepodpisane, jest maszyną -zależne, ale znaki do wydruku są zawsze dodatnie.
signed char
i unsigned char
oba reprezentują 1 bajt, ale mają różne zakresy.
Type | range
-------------------------------
signed char | -128 to +127
unsigned char | 0 to 255
W signed char
jeśli weźmiemy pod uwagę char letter = 'A'
, „A” ma reprezentować binarnie z 65 w ASCII/Unicode
, przypadku 65 mogą być przechowywane, -65 może być również przechowywane. Nie ma tam ujemnych wartości binarnych, więc ASCII/Unicode
nie musisz się martwić o wartości ujemne.
Przykład
#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;
}
Wynik -:
Signed char(255) : -1
Unsigned char(255) : 255
Signed char(-128) : -128
Unsigned char(-128) : 128