Czy rzuty pomiędzy signed i unsigned int zachowują dokładny wzorzec bitowy zmiennej w pamięci?
Chcę przekazać 32-bitową, podpisaną liczbę całkowitą x
przez gniazdo. Aby odbiorca wiedział, jakiej kolejności bajtów się spodziewać, wywołuję htonl(x)
przed wysłaniem. htonl
oczekuje jednak uint32_t
i chcę być pewien, co się stanie, gdy rzucę moje int32_t
na uint32_t
.
int32_t x = something;
uint32_t u = (uint32_t) x;
Czy zawsze jest tak, że bajty w x
i u
będą dokładnie takie same? A co z oddaniem:
uint32_t u = something;
int32_t x = (int32_t) u;
Zdaję sobie sprawę, że wartości ujemne rzucane na duże wartości niepodpisane ale to nie ma znaczenia, ponieważ ja tylko rzucam się na drugi koniec. Jednak jeśli cast MES z rzeczywistymi bajtami, to nie mogę być pewien, że casting back zwróci tę samą wartość.
3 answers
Ogólnie rzecz biorąc, odlewanie w C jest określone pod względem wartości, a nie wzorców bitowych - pierwsze zostaną zachowane( jeśli to możliwe), ale drugie niekoniecznie. W przypadku dwóch reprezentacji dopełniacza bez wypełnienia-co jest obowiązkowe dla stałych-z typami całkowitymi-rozróżnienie to nie ma znaczenia i rzut rzeczywiście będzie noop.
Ale nawet jeśli konwersja z signed do unsigned zmieniłaby wzorzec bitowy, konwersja z powrotem przywróciłaby wartość oryginalna - z zastrzeżeniem, że konwersja spoza zakresu unsigned to signed jest zdefiniowana w implementacji i może wywołać sygnał na przepełnienie.
Aby uzyskać pełną przenośność (co prawdopodobnie będzie przesadą), musisz użyć type punning zamiast conversion. Można to zrobić na dwa sposoby:
Przez wskaźnik rzutów, czyli
uint32_t u = *(uint32_t*)&x;
Na które należy uważać, ponieważ może to naruszać skuteczne zasady typowania (ale jest dobre dla podpisanych/niepodpisanych wariantów typów całkowitych) lub poprzez związki zawodowe, czyli
uint32_t u = ((union { int32_t i; uint32_t u; }){ .i = x }).u;
, który może być również użyty do np. konwersji z double
na uint64_t
, czego nie można zrobić za pomocą rzutów wskaźnikowych, jeśli chcesz uniknąć niezdefiniowanego zachowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-21 09:36:31
Odlewy są używane w języku C, aby oznaczać zarówno "konwersję typu", jak i"disambiguację typu". Jeśli masz coś takiego
(float) 3
Wtedy jest to konwersja typu, A rzeczywiste bity się zmieniają. Jeśli powiesz
(float) 3.0
To rodzaj disambiguation.
Zakładając reprezentację dopełniacza (Zobacz komentarze poniżej), kiedy rzucisz int
na unsigned int
, wzór bitowy nie zostanie zmieniony, tylko jego znaczenie semantyczne; jeśli oddasz go z powrotem, wynik będzie zawsze poprawny. Wpada do przypadek disambiguation typu, ponieważ żadne bity nie są zmieniane, tylko sposób, w jaki komputer je interpretuje.
Zauważ, że teoretycznie dopełniacz 2 nie może być użyty, a unsigned
i signed
mogą mieć bardzo różne reprezentacje, a rzeczywisty wzór bitowy może się w tym przypadku zmienić.
Jednak od C11 (aktualnego standardu C) masz gwarancję, że sizeof(int) == sizeof(unsigned int)
:
Powiedziałbym, że w praktyce można założyć, że jest to bezpieczne.(§6.2.5/6) dla każdego z podpisanych typów całkowitych istnieje odpowiedni (ale inny) niepodpisany Typ integer (oznaczany z słowo kluczowe unsigned), które wykorzystuje taką samą ilość miejsca (w tym informacja o podpisie) i ma takie same wymagania [...]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-21 09:42:27
Powinno to być zawsze bezpieczne, ponieważ typy intXX_t
są gwarantowane w dopełniaczu dwójkowym Jeśli istnieją:
7.20.1.1 typy liczby całkowitej o dokładnej szerokości nazwa typedef intN_t oznacza znakowany Typ liczby całkowitej z szerokością N, bez bitów wypełnienia i dwójką reprezentacja uzupełniająca. Tak więc int8_t oznacza taką podpisaną liczbę całkowitą Typ o szerokości dokładnie 8 bitów.
Teoretycznie konwersja wsteczna z uint32_t
na {[2] } jest zdefiniowana, tak jak dla wszystkie konwersje unsigned
do signed
. Ale nie mogę sobie wyobrazić, że platforma zrobiłaby inaczej niż się spodziewasz.
Jeśli chcesz być naprawdę pewien tego, nadal możesz wykonać tę konwersję ręcznie. Musisz tylko sprawdzić wartość > INT32_MAX
, a potem trochę policzyć. Nawet jeśli robisz to systematycznie, porządny kompilator powinien być w stanie to wykryć i zoptymalizować.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-21 10:04:29