Od czasu do czasu ktoś na SO wskazuje, że char
(inaczej „bajt”) niekoniecznie musi mieć 8 bitów .
Wygląda na to, że 8-bitowe char
jest prawie uniwersalne. Pomyślałbym, że w przypadku platform głównego nurtu konieczne jest posiadanie 8-bitowego, char
aby zapewnić jego żywotność na rynku.
Jakie platformy używają obecnie i w przeszłości platformy, char
która nie ma 8 bitów i dlaczego miałyby różnić się od „normalnych” 8 bitów?
Pisząc kod i myśląc o obsłudze wielu platform (np. Dla bibliotek ogólnego użytku), na co warto zwrócić uwagę na platformach innych niż 8-bitowe char
?
W przeszłości natknąłem się na kilka procesorów DSP Analog Devices, które char
mają 16 bitów. Podejrzewam, że procesory DSP są trochę niszową architekturą. (Z drugiej strony, w tamtym czasie ręcznie kodowany asembler z łatwością pokonał to, co mogły zrobić dostępne kompilatory C, więc tak naprawdę nie miałem zbyt dużego doświadczenia z C na tej platformie).
źródło
Odpowiedzi:
char
jest również 16-bitowy w procesorach DSP Texas Instruments C54x, które pojawiły się na przykład w OMAP2. Istnieją inne procesory DSP z 16 i 32 bitamichar
. Myślę, że słyszałem nawet o 24-bitowym DSP, ale nie pamiętam co, więc może sobie to wyobraziłem.Inną kwestią jest to, że POSIX wymaga
CHAR_BIT == 8
. Więc jeśli używasz POSIX, możesz to założyć. Jeśli ktoś później będzie musiał przenieść twój kod do prawie implementacji POSIX, tak się składa, że ma funkcje, których używasz, ale inny rozmiarchar
, to jego pech.Ogólnie rzecz biorąc, myślę, że prawie zawsze łatwiej jest obejść ten problem niż o nim pomyśleć. Po prostu wpisz
CHAR_BIT
. Jeśli chcesz mieć dokładnie 8-bitowy typ, użyjint8_t
. Twój kod głośno nie będzie się kompilował na implementacjach, które go nie zapewniają, zamiast dyskretnie używać rozmiaru, którego się nie spodziewałeś. Przynajmniej, gdybym trafił na przypadek, w którym miałbym dobry powód, żeby to założyć, to bym to potwierdził.źródło
assert()
(jeśli to miałeś na myśli), użyłbym#if CHAR_BIT != 8
...#error "I require CHAR_BIT == 8"
...#endif
static_assert()
?Nie chodzi o to, że „warto się nad czymś zastanowić”, ale o granie według zasad. Na przykład w C ++ standard mówi, że wszystkie bajty będą miały „co najmniej” 8 bitów. Jeśli Twój kod zakłada, że bajty mają dokładnie 8 bitów, naruszasz standard.
To może wydawać się teraz głupie - „ oczywiście wszystkie bajty mają 8 bitów!”, Słyszę, jak mówisz. Ale wielu bardzo inteligentnych ludzi oparło się na założeniach, które nie były gwarancją, a potem wszystko się zepsuło. Historia jest pełna takich przykładów.
Na przykład większość deweloperów wczesnych lat 90. założyło, że określone opóźnienie taktowania procesora bez operacji, zajmujące stałą liczbę cykli, zajmie stałą ilość czasu, ponieważ większość procesorów konsumenckich miała mniej więcej taką samą moc. Niestety komputery bardzo szybko stały się szybsze. To spowodowało powstanie pudełek z przyciskami „Turbo” - których celem, jak na ironię, było spowolnienie komputera, aby gry wykorzystujące technikę opóźnienia czasowego mogły być odtwarzane z rozsądną prędkością.
Jeden komentator zapytał, gdzie w standardzie jest napisane, że znak musi mieć co najmniej 8 bitów. Jest w sekcji 5.2.4.2.1 . Ta sekcja definiuje
CHAR_BIT
liczbę bitów w najmniejszej adresowalnej encji i ma domyślną wartość 8. Mówi się również:Zatem każda liczba równa 8 lub wyższa nadaje się do zastąpienia przez implementację do
CHAR_BIT
.źródło
char
jest ich więcej niż 64, ale mniej niż 128, więc wystarczyłoby 7 bitów.Maszyny o architekturze 36-bitowej mają 9-bitowe bajty. Według Wikipedii maszyny o architekturze 36-bitowej obejmują:
źródło
Kilka z nich jestem świadomy:
źródło
char
typu? Wiem, że biblioteki systemowe obsługiwały tylko szerokie wersje funkcji, które pobierają ciągi znaków, i że przynajmniej niektóre wersje WinCE usunęły funkcje ciągów ANSI, takie jak strlen, aby uniemożliwić ci obsługę łańcuchów znaków. Ale czy naprawdę w ogóle nie miał typu char? Co to byłosizeof(TCHAR)
? Jaki typ powrócił Malloc? Jakbyte
zaimplementowano typ Java ?Nie ma czegoś takiego jak całkowicie przenośny kod. :-)
Tak, mogą istnieć różne rozmiary bajtów / znaków. Tak, mogą istnieć implementacje C / C ++ dla platform z bardzo nietypowymi wartościami
CHAR_BIT
iUCHAR_MAX
. Tak, czasami można napisać kod niezależny od rozmiaru znaku.Jednak prawie każdy prawdziwy kod nie jest samodzielny. Np. Możesz pisać kod, który wysyła binarne wiadomości do sieci (protokół nie jest ważny). Możesz zdefiniować struktury, które zawierają niezbędne pola. Wtedy musisz to serializować. Samo binarne kopiowanie struktury do bufora wyjściowego nie jest przenośne: generalnie nie znasz ani kolejności bajtów dla platformy, ani wyrównania elementów struktury, więc struktura po prostu przechowuje dane, ale nie opisuje sposobu, w jaki dane powinny być serializowane .
Dobrze. Możesz wykonywać transformacje kolejności bajtów i przenosić składowe struktury (np.
uint32_t
Lub podobne) za pomocąmemcpy
do bufora. Dlaczegomemcpy
? Ponieważ istnieje wiele platform, na których nie można zapisać 32-bitowego (16-bitowego, 64-bitowego - bez różnicy), gdy adres docelowy nie jest prawidłowo wyrównany.Tak więc wiele już zrobiłeś, aby zapewnić przenośność.
A teraz ostatnie pytanie. Mamy bufor. Dane z niego przesyłane są do sieci TCP / IP. Taka sieć przyjmuje 8-bitowe bajty. Pytanie brzmi: jakiego typu powinien być bufor? Jeśli twoje znaki są 9-bitowe? Jeśli są 16-bitowe? 24? Może każdy znak odpowiada jednemu 8-bitowemu bajtowi wysłanemu do sieci, a używanych jest tylko 8 bitów? A może wiele bajtów sieciowych jest pakowanych w znaki 24/16/9-bitowe? To jest pytanie i trudno uwierzyć, że istnieje jedna odpowiedź, która pasuje do wszystkich przypadków. Wiele rzeczy zależy od implementacji gniazda na platformie docelowej.
Więc o czym mówię. Zwykle do pewnego stopnia kod można stosunkowo łatwo przenieść do postaci przenośnej . Jest to bardzo ważne, jeśli spodziewasz się używania kodu na różnych platformach. Jednak poprawa przenośności wykraczająca poza ten środek to rzecz, która wymaga dużego wysiłku i często daje niewiele , ponieważ rzeczywisty kod prawie zawsze zależy od innego kodu (implementacja gniazda w powyższym przykładzie). Jestem pewien, że dla około 90% kodu możliwość pracy na platformach z bajtami innymi niż 8-bitowe jest prawie bezużyteczna, gdyż wykorzystuje środowisko związane z 8-bitami. Po prostu sprawdź rozmiar bajtu i przeprowadź asercję czasu kompilacji. Prawie na pewno będziesz musiał wiele przepisać na bardzo nietypową platformę.
Ale jeśli Twój kod jest wysoce „samodzielny” - dlaczego nie? Możesz napisać to w sposób, który pozwala na różne rozmiary bajtów.
źródło
unsigned char
wartość, nie powinno być problemów z przenoszeniem, chyba że kod używa sztuczek aliasingu zamiast przesunięć w celu konwersji sekwencji oktetów na / z większych typów całkowitych. Osobiście uważam, że standard C powinien definiować elementy wewnętrzne do pakowania / rozpakowywania liczb całkowitych z sekwencji krótszych typów (najczęściejchar
) przechowujących stałą gwarantowaną dostępną liczbę bitów na sztukę (8 naunsigned char
, 16 naunsigned short
lub 32 naunsigned long
).Wygląda na to, że nadal można kupić IM6100 (czyli PDP-8 na chipie) z magazynu. To jest architektura 12-bitowa.
źródło
Wiele układów DSP ma 16- lub 32-bitowe
char
. TI rutynowo wytwarza na przykład takie chipy .źródło
Cytat z http://en.wikipedia.org/wiki/Byte#History
Nie jestem pewien co do innych języków.
http://en.wikipedia.org/wiki/IBM_7030_Stretch#Data_Formats
Definiuje bajt na tym komputerze o zmiennej długości
źródło
Rodzina DEC PDP-8 miała 12-bitowe słowo, chociaż zwykle używałeś 8-bitowego ASCII do wyjścia (głównie na Teletype). Jednak był też 6-bitowy kod znaków, który pozwalał zakodować 2 znaki w jednym 12-bitowym słowie.
źródło
Po pierwsze, znaki Unicode są dłuższe niż 8-bitowe. Jak ktoś wcześniej wspomniał, specyfikacja C definiuje typy danych według ich minimalnych rozmiarów. Użyj
sizeof
i wartości wlimits.h
jeśli chcesz przesłuchać typy danych i dowiedzieć się, jaki dokładnie mają rozmiar dla Twojej konfiguracji i architektury.Z tego powodu staram się trzymać typów danych, na przykład
uint16_t
gdy potrzebuję typu danych o określonej długości bitowej.Edycja: Przepraszamy, początkowo źle odczytałem twoje pytanie.
Specyfikacja C mówi, że
char
obiekt jest „wystarczająco duży, aby przechowywać dowolny element zestawu znaków wykonania”.limits.h
podaje minimalny rozmiar 8 bitów, ale definicja pozostawia maksymalny rozmiarchar
otwarcia.W związku z tym a
char
jest co najmniej tak długi, jak największy znak z zestawu wykonawczego Twojej architektury (zazwyczaj zaokrąglany w górę do najbliższej 8-bitowej granicy). Jeśli twoja architektura ma dłuższe rozkazy, twójchar
rozmiar może być dłuższy.Historycznie rzecz biorąc, kod operacyjny platformy x86 miał długość jednego bajtu, więc
char
początkowo był to wartość 8-bitowa. Obecne platformy x86 obsługują opkody dłuższe niż jeden bajt, alechar
jest on utrzymywany na 8 bitach, ponieważ od tego są uzależnieni programiści (i duże ilości istniejącego kodu x86).Myśląc o obsłudze wielu platform, skorzystaj z typów zdefiniowanych w
stdint.h
. Jeśli korzystasz (na przykład) na uint16_t, wtedy można mieć pewność, że wartość ta jest niepodpisany wartość 16-bitowa na dowolnej architekturze, czy odpowiadający wartości 16-bitowej dochar
,short
,int
, lub coś innego. Większość ciężkiej pracy została już wykonana przez ludzi, którzy napisali Wasze biblioteki kompilatorów / standardowych.Jeśli potrzebujesz znać dokładny rozmiar a,
char
ponieważ wykonujesz jakieś operacje sprzętowe niskiego poziomu, które tego wymagają, zwykle używam typu danych, który jest wystarczająco duży, aby pomieścić achar
na wszystkich obsługiwanych platformach (zwykle wystarcza 16 bitów) i uruchamiam wartość poprzezconvert_to_machine_char
procedurę, gdy potrzebuję dokładnej reprezentacji maszyny. W ten sposób kod specyficzny dla platformy jest ograniczony do funkcji interfejsu i przez większość czasu mogę używać normalnegouint16_t
.źródło
magiczne liczby pojawiają się np. podczas przesuwania;
większość z nich można obsłużyć w prosty sposób, używając CHAR_BIT i np. UCHAR_MAX zamiast 8 i 255 (lub podobnych).
miejmy nadzieję, że Twoja implementacja je definiuje :)
to są „typowe” problemy .....
inną pośrednią kwestią jest to, że masz:
może to zająć „tylko” (w najlepszym przypadku) 24 bity na jednej platformie, ale może zająć np. 72 bity w innym miejscu .....
jeśli każdy uchar trzymał "flagi bitowe" i każdy uchar miał tylko 2 "znaczące" bity lub flagi, których aktualnie używasz, i zorganizowałeś je tylko w 3 uchary dla "przejrzystości", wtedy może to być stosunkowo "bardziej marnotrawne" np. platforma z 24-bitowymi ucharami .....
nic bitfieldów nie może rozwiązać, ale mają inne rzeczy, na które trzeba uważać ....
w tym przypadku tylko jedno wyliczenie może być sposobem na uzyskanie „najmniejszej” liczby całkowitej, której faktycznie potrzebujesz ....
może nie jest to prawdziwy przykład, ale takie rzeczy „ugryzły” mnie podczas przenoszenia / grania z jakimś kodem…
tylko fakt, że jeśli uchar jest trzykrotnie większy niż to, co jest "normalnie" oczekiwane, 100 takich struktur może marnować dużo pamięci na niektórych platformach ..... gdzie "normalnie" to nie jest wielka sprawa .... .
więc rzeczy nadal mogą być "zepsute" lub w tym przypadku "marnować dużo pamięci bardzo szybko" z powodu założenia, że uchar jest "niezbyt marnotrawny" na jednej platformie w stosunku do dostępnej pamięci RAM, niż na innej platformie ... ..
problem może być bardziej widoczny, np. również w przypadku int, lub innych typów, np. masz strukturę, która wymaga 15 bitów, więc umieszczasz ją w int, ale na innej platformie int ma 48 bitów lub cokolwiek .... .
„normalnie” możesz podzielić to na 2 uchary, ale np. z 24-bitowym uchar potrzebujesz tylko jednego .....
więc wyliczenie może być lepszym rozwiązaniem „ogólnym” ...
zależy jednak od tego, w jaki sposób uzyskujesz dostęp do tych bitów :)
więc mogą istnieć "wady projektowe", które odwracają ich głowę ... nawet jeśli kod może nadal działać / działać dobrze, niezależnie od rozmiaru uchar lub uint ...
są takie rzeczy, na które trzeba uważać, mimo że w Twoim kodzie nie ma „magicznych liczb” ...
mam nadzieję, że to ma sens :)
źródło
enum
może być mniejszy niż inne rodzime typy? Czy wiesz, że domyślnie jest to ta sama pamięć, coint
? „masz jakąś strukturę, która wymaga 15 bitów, więc umieszczasz ją w int, ale na innej platformie int ma 48 bitów lub cokolwiek .....” - więc#include <cstdint>
zróbint16_t
z tego największą szansę na zminimalizowanie użycia bitów . Naprawdę nie jestem pewien, co myślisz, że mówisz wśród tych wszystkich elips.ints miały 16 bitów (pdp11 itp.). Przejście na architekturę 32-bitową było trudne. Ludzie są coraz lepsi: prawie nikt nie zakłada, że wskaźnik będzie już pasował na długo (nie masz racji?). Lub zapisuj przesunięcia, sygnatury czasowe lub ...
8-bitowe znaki są już pewnym anachronizmem. Potrzebujemy już 32 bitów, aby pomieścić wszystkie zestawy znaków świata.
źródło
char
jest teraz nieco osobliwa w czasach Unicode. Bardziej interesują mnie jednostki 8-bitowe (oktety), gdy mam do czynienia z danymi binarnymi, np. Przechowywanie plików, komunikacja sieciowa.uint8_t
jest bardziej przydatne.