W C / C ++ do czego unsigned charsłuży an ? Czym różni się od zwykłego char?
W C / C ++ do czego unsigned charsłuży an ? Czym różni się od zwykłego char?
Odpowiedzi:
W C ++ istnieją trzy różne typy znaków:
charsigned charunsigned charJeś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. sizeofnadal będzie zgłosić swoją wielkość, jak1 - co oznacza, że mogłyby mieć sizeof (char) == sizeof (long) == 1.
sizeofponieważ nie jest to funkcja, ale operator. Jeszcze lepszym stylem jest pominięcie nawiasu przy przyjmowaniu wielkości zmiennej. sizeof *plub 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ć signedlub unsigned, więc musisz jawnie o to poprosić signed charlub od unsigned chartego 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 chari unsigned charjest taka, jak można się spodziewać. Na większości platform signed charbędzie 8-bitową liczbą uzupełnień do dwóch, od -128do 127, i unsigned charbędzie 8-bitową liczbą całkowitą bez znaku ( 0do 255). Uwaga standard nie wymaga, aby chartypy miały 8 bitów, tylko ten sizeof(char)zwrot 1. Możesz dostać się do liczby bitów w znaku z CHAR_BITin 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_ti uint8_tjeśli naprawdę chcesz reprezentować małe liczby całkowite.
CHAR_BITzgodnie 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 charudziału w ustalaniu wartości, jeśli jakiekolwiek unsigned char obiektu. Po drugie, unsigned charjest wyraźnie określony jako niepodpisany.
Teraz rozmawiałem z kimś o tym, co się dzieje, kiedy konwertujesz wartość -1typu int na unsigned char. Odrzucił ideę, że wynikowe unsigned charma 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.3p2w 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 -1są jednym przed konwersją. Co więc mamy, abyśmy mogli twierdzić, że wynikowy unsigned charma wszystkie CHAR_BITbity zmienione na 1?
UCHAR_MAX+1na -1przyniesie wartość w zakresie, a mianowicieUCHAR_MAXWłaściwie to wystarczy! Tak więc, kiedy tylko chcesz mieć unsigned charwszystkie 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)-1to jakiś idiom. ~0nie jest.
int x = 1234i 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 00000000LSB na pierwszym miejscu. Teraz główna część. jeśli użyję printf("%d" , *p). printfodczyta pierwszy bajt 11010010tylko wyjście -46, ale 11010010jest 210tak 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 charjest 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 charwartoś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 charzakres 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 chararytmetyki. Jednak jeśli wartości byłyby signed chars (127, -128, -128), otrzymalibyśmy (-99, -99, -99), co byłoby (29, 29, 29) w naszej unsigned charprzestrzeni, 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_ti uint8_t.
int8_ti uint8_tsą opcjonalne i nie zdefiniowano na architekturach gdzie wielkość bajt nie jest dokładnie 8 bitów. I odwrotnie, signed chari unsigned charzawsze są dostępne i gwarantują, że mieszczą co najmniej 8 bitów. Może to być powszechny sposób, ale nie najbezpieczniejszy .
signed chari unsigned char? A może poleciłbyś lepszą „bezpieczniejszą” alternatywę w tym konkretnym przypadku? Na przykład trzymać się „prawdziwych” liczb całkowitych signed inti unsigned intzamiast tego z jakiegoś powodu?
signed chari unsigned charjest 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.
chari unsigned charnie 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 charma 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 charjest bajtem bez znaku (od 0 do 255). Być może myślisz o charbyciu „postacią”, ale tak naprawdę jest to wartość liczbowa. Normalny charjest 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_MINa CHAR_MAXgdy znak bez znaku zapewnia podwójny zakres na dodatnim końcu. Na przykład, jeśli CHAR_BITjest to 8, zakres wartości regularnych charjest gwarantowany tylko na [0, 127] (ponieważ może być podpisany lub niepodpisany), podczas gdy unsigned charbę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 chari 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 charOR, a następnie kontynuować korzystanie ++ptrz 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 charjest sercem wszystkich sztuczek. W prawie WSZYSTKIM kompilatorze dla WSZYSTKICH platform jest unsigned charto 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 chartypy: 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_titd 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 signedlub unsignedmoż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 chari unsigned charoba reprezentują 1 bajt, ale mają różne zakresy.
Type | range
-------------------------------
signed char | -128 to +127
unsigned char | 0 to 255
W signed charjeś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/Unicodenie 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