Czy mogę zamienić niepodpisany znak na znak i odwrotnie?
Chcę użyć funkcji, która oczekuje takich danych:
void process(char *data_in, int data_len);
Więc tak naprawdę obsługuje tylko niektóre bajty.
Ale wygodniej mi jest pracować z „unsigned char”, jeśli chodzi o nieprzetworzone bajty (z jakiegoś powodu bardziej poprawne jest zajmowanie się tylko dodatnimi wartościami od 0 do 255), więc moje pytanie brzmi:
Czy zawsze mogę bezpiecznie przekazać
unsigned char *do tej funkcji?
Innymi słowy:
- Czy jest zagwarantowane, że mogę bezpiecznie konwertować (rzucać) między char i unsigned char, do woli, bez utraty informacji
- Czy mogę bezpiecznie konwertować (rzucać) między wskaźnikami na znak i znak bez znaku do woli, bez utraty informacji
Bonus: czy odpowiedź jest taka sama w C i C ++?
Nie znaleziono powiązanych wyników
Zaproszony:
Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się
6 odpowiedzi
Anonimowy użytkownik
Potwierdzenie od:
1) legalność przekształcenia
Konwersja między i (dla niektórych typów ) w dowolnym kierunku jest zwykle możliwa, ponieważ oryginalny typ można najpierw przekonwertowane na (jest to standardowa konwersja, §4.10), a można przekonwertować na typ docelowy za pomocą jawnego (§5.2.9/trzynaście):
Można to skrócić (§5.2.10/7) w następujący sposób:
ponieważ jest typem standardowego układu (§3.9.1/7.8 i §3.9/9), a podpisywanie nie zmienia wyrównania (§3.9.1/1). Można go również napisać jako obsadę w stylu C:
Ponownie działa to w obie strony, od do iz powrotem. Istnieje również gwarancja, że jeśli zastosujesz tę procedurę w jedną stronę, a potem z powrotem, wartość wskaźnika (czyli adres, na który wskazuje) nie zmieni się (§5.2.10/7).
Dotyczy to nie tylko konwersji między i , ale także / i / . ( , i to formalnie trzy różne typy, §3.9.1/1.)
Żeby było jasne, nie ma znaczenia, której z trzech metod rzucania używasz, ale powinieneś użyć jednej. Zwykłe przekazywanie wskaźnika nie zadziała, ponieważ konwersja, chociaż legalna, nie jest konwersją standardową, więc nie zostanie wykonana niejawnie (kompilator zgłosi błąd, jeśli spróbujesz).
2) jasno określony dostęp do wartości
Co się stanie, jeśli wyłuskujemy wskaźnik wewnątrz funkcji, to znaczy wykonujemy , aby uzyskać wartość glvalue dla bazowego znaku; czy jest dobrze zdefiniowany i legalny? Odpowiednią zasadą jest tutaj ścisła reguła antyaliasingu (§ 3.10/10):
Jeśli program próbuje uzyskać dostęp do przechowywanej wartości obiektu za pośrednictwem
glvalue
typu innego niż jeden z poniższych, to zachowanie jest niezdefiniowane:
- [...]
- typ, który jest typem ze znakiem lub bez znaku odpowiadającym typowi dynamicznemu obiektu,
- [...]
- wpisz
lub .
Zatem dostęp do (lub ) za pośrednictwem (lub ) i na odwrót nie jest zakazana przez tę zasadę - powinieneś to zrobić bez żadnych problemów.3) wartości wynikowe
Czy po wyłuskaniu konwertowanego typu wskaźnika możesz pracować z uzyskaną wartością? Należy pamiętać, że konwersja i dereferencja wskaźnika opisanego powyżej jest równoznaczne z reinterpretacją (nie modyfikowaniem!) Wzorca bitowego przechowywanego pod adresem symbolu. Więc co się dzieje, gdy wzorzec bitowy dla znaku ze znakiem jest interpretowany jako wzorzec dla znaku bez znaku (lub odwrotnie)?
Przechodząc od niepodpisanego do podpisanego
typowy efekt
oznacza, że dla wartości od 0 do 128 nic się nie dzieje, a wartości powyżej 128 stają się ujemne. Podobnie w odwrotnej kolejności: podczas przechodzenia od znaku do znaku bez znaku wartości ujemne będą wyświetlane jako wartości większe niż 128.
Ale to zachowanie jest w rzeczywistości
sprawa nie jest gwarantowana
standard. Jedyną standardową gwarancją jest to, że dla wszystkich trzech typów, , i , wszystkie bity (niekoniecznie 8 przez znak sposób) są używane do reprezentowania wartości. Dlatego jeśli interpretujesz jedną jako drugą, wykonasz wiele kopii, a następnie zapiszesz je z powrotem w ich pierwotnej lokalizacji, możesz być pewien, że nie nastąpi utrata informacji (tak jak żądałeś), ale niekoniecznie będziesz wiedział, jakie te wartości Są włączone. Mają na myśli (przynajmniej nie w całkowicie przenośny sposób).
Anonimowy użytkownik
Potwierdzenie od:
Ponieważ przetwarzasz bajty, aby pokazać zamiar, lepiej byłoby zadeklarować jako
[Jak zauważył redaktor: zwykły może być podpisany lub niepodpisany. Standardy C i C ++ wyraźnie na to zezwalają (jest to zawsze typ oddzielny od lub , ale ma taki sam zakres jak jeden z nich)]
Anonimowy użytkownik
Potwierdzenie od:
przeciwnie
nie ma problemu. Jeśli uruchomisz poniższy kod i porównasz go z tabelą ASCII (zobacz.
http://www.asciitable.com/
http://www.asciitable.com/), możesz sam zobaczyć dowód i jak C/C ++ radzi sobie z konwersjami - działają dokładnie tak samo:
Nie opublikuję wyjścia, ponieważ jest w nim za dużo wierszy! Na wyjściu można zauważyć, że w pierwszej połowie każdej sekcji, tj. Zaczynając od i = 0: 127, następuje konwersja znaków na znaki bez znaku i
przeciwnie
działa dobrze bez żadnych zmian i strat.
Jednak z i = 128: 255 znaków i znaków bez znaku nie można odtworzyć lub będziesz mieć inne dane wyjściowe, ponieważ unsigned char przechowuje wartości z [0: 256], a char przechowuje wartości w interwale [-128: 127 ]) ... Jednak zachowanie w drugiej połowie jest nieistotne, ponieważ w C/C ++ generalnie prowadzisz tylko ze znakami/bez znaku jako znakami ASCII, które mogą przyjmować tylko 128 różnych wartości, a pozostałe 128 wartości (dodatnie dla chars lub minus dla znaków bez znaku) nigdy nie są używane.
Jeśli nigdy nie umieścisz wartości w znaku, który nie reprezentuje znaku, i nigdy nie umieścisz wartości w znaku bez znaku, który nie reprezentuje znaku, wszystko będzie w porządku!
Dodatkowo: nawet jeśli użyjesz UTF-8 lub innego kodowania (dla znaków specjalnych) w swoich ciągach C/C ++, wszystko, co ma tego rodzaju rzutowanie, będzie OK, na przykład używając UTF-8 (ref.
http://lwp.interglacial.com/appf_01.htm
http://lwp.interglacial.com/appf_01.htm
):
wynik tego kodu będzie następujący:
serca (♥)
moje diamenty (♦)
kluby (♣)
łopaty (♠)
nawet jeśli rzucisz każdą z jego postaci na znaki bez znaku.
Więc:
Anonimowy użytkownik
Potwierdzenie od:
Jeśli chcesz przekonwertować na wewnątrz funkcji, po prostu przypisz wartość do zmienna lub rzutowanie wartości na .
Jeśli chcesz przekonwertować na bez utraty danych, jest to trochę trudne, ale nadal możliwe:
Ta funkcja konwertuje na , dzięki czemu wartość zwracana może zostać przekonwertowana z powrotem na tę samą wartość co parametr.
Anonimowy użytkownik
Potwierdzenie od:
przejścia
między a są bezpieczne i nawet jeśli są rzutowane między nimi, jak w c ++.
Jednak rozważ następujący przykład kodu:
Wyjście:
0
255
-1
-1Cały kod wewnątrz i jest po prostu
IDENTICAL
... Jedyna różnica to brak znaku i podpis. Ten przykład pokazuje, że kod w
czarna skrzynka
naprawdę pod wpływem
SIGN
i
nic
nie jest gwarantowane między wywoływanym a dzwoniącym.
Dlatego powiedziałbym, że ma to zastosowanie tylko do
przechodzący
ale żadne inne możliwości nie są gwarantowane.
Anonimowy użytkownik
Potwierdzenie od: