Co ma znaczyć casting?

18

Podczas kodowania w językach niskiego poziomu, takich jak CI, zauważ, że rzutowanie czasami oznacza „reinterpretuj te bajty, tak jakby zawsze był tego innego typu”, a innym razem jako „inteligentnie przekonwertuj tę wartość na ten inny typ”.

Jakie jest pierwotne znaczenie tego słowa i czy istnieje spójność, kiedy należy spodziewać się konwersji, a kiedy surowej interpretacji?

Alexander Torstling
źródło
co jest w artykule Wikipedii , którego nie rozumiesz? „ Konwersja typu , rzutowanie typu i przymus to różne sposoby, pośrednio lub jawnie, zmiany encji jednego typu danych na inny ... Każdy język programowania ma swoje własne zasady dotyczące konwersji typów…”
gnat
Pierwotne znaczenie słowa „rzucić” nie ma nic wspólnego z programowaniem, patrz tutaj merriam-webster.com/dictionary/cast
Doc Brown
1
Wiele (głównie z perspektywy języka zarządzanego) na blogu Erica Lipperta w kategorii operatorów obsady
AakashM
1
@gnat: Nie jestem pewien, czy to poważne pytanie, czy próba trollingu. Ale chciałbym wiedzieć, jak wiedzieć, co zrobi kompilator: konwertować, rzutować lub wymuszać? Jakie są podstawowe zasady?
Alexander Torstling
1
@DocBrown Myślę, że termin castw sensie komputerowym jest bardziej podobny do odlewania w sensie metalurgicznym, w którym kształt stopionego metalu ulega reformie po wlaniu do formy: britannica.com/EBchecked/topic/377665/metallurgy/81884/Casting
KChaloux

Odpowiedzi:

10

Przesyłanie w C jest wyjątkowe, w przeciwieństwie do innych języków. Nigdy też nie jest inteligentny.

Rzutowanie w C konwertuje wartości z jednego typu na inny przy użyciu dokładnie zdefiniowanych reguł. Jeśli naprawdę potrzebujesz wiedzieć, przeczytaj standard. W przeciwnym razie główne punkty to:

  1. Konwersja między typami liczb całkowitych zachowuje wartość, jeśli to możliwe. Jeśli miejsce docelowe ma więcej bitów, rozszerza się i jest ogólnie bezpieczne, ale może obejmować rozszerzenie znaku. Jeśli węższy, bity zostaną utracone.
  2. Konwersja między typami wskaźników zachowuje wartość wskaźnika, ale wyniki są często niezdefiniowane, często nieprzenośne i często przydatne w zaawansowanych scenariuszach.
  3. Konwersja między typami liczb całkowitych a wskaźnikami jest OK, jeśli liczba całkowita jest wystarczająco duża i zachowuje wzorzec bitowy (cokolwiek by to nie znaczyło). Jeśli liczba całkowita jest zbyt mała, wynik jest niezdefiniowany, ale nie jest użyteczny. Z reguły „długa” jest wystarczająco szeroka dla „pustki *”, ale nie ma gwarancji! Wskaźniki utworzone w ten sposób mogą być nieważne na wiele interesujących sposobów.
  4. Konwersja między typami zmiennoprzecinkowymi i liczbami całkowitymi są konwersjami arytmetycznymi zdefiniowanymi przez odpowiednią procedurę biblioteczną (z obcięciem, bez zaokrąglania).
  5. Możesz rzucić wartość zwracaną funkcji, aby anulować. Nigdy nie mam. Nic nie robi.

Niektóre rzutowania są stosowane domyślnie, a w niektórych kompilator generuje ostrzeżenie. Najlepiej posłuchaj ostrzeżeń!

Słownikową definicję obsady najlepiej zignorować, ponieważ jest nieprzydatna. Wiele obsad jest lepiej opisanych terminami konwersja lub przymus, więc warto je też poznać.

C ++ jest DUŻO bardziej skomplikowany, ale o to nie pytałeś, prawda?

david.pfx
źródło
Interesują mnie praktyczne zasady, a nie drobne szczegóły, ale interesują mnie inne języki niż C, jeśli to pomaga wyjaśnić sprawę.
Alexander Torstling
2
To, co tu podałem, jest tak ogólne, jak rozsądne. Aby napisać prawdziwy, profesjonalny kod C / C ++ niskiego poziomu, najdrobniejsze są szczegóły. Większość języków po prostu nie ma tego rodzaju problemów w konwersji typu. Przepraszam, jeśli to nie rozwiąże twojego problemu.
david.pfx
Tyle że konwersja z powrotem T*na void*iz powrotem jest zawsze dobrze zdefiniowana.
Miles Rout
@Miles: Właściwie konwersja T * do dowolnego U * i wstecz jest wymagana, aby zachować oryginalną wartość wskaźnika. W mojej odpowiedzi powiedziałem tylko „często nieokreślony”, aby krótko mówiąc, ponieważ niektóre szczegóły są bardzo nieuporządkowane.
david.pfx
1
@ supercat: Patrz n1570 S6.3.2.3. Konwersja / tripping tripping między T *, U * i void * zawsze zachowuje wartość wskaźnika z jednym wyjątkiem. Jeśli jakikolwiek T * nie jest poprawnie wyrównany, jest to niezdefiniowane zachowanie. Akceptuję twój punkt widzenia, ale tylko w tym zakresie.
david.pfx
2

Ta część słownika Webster zawiera poprawną definicję:

a: aby nadać kształt (substancji) przez wlanie płynnej lub plastikowej formy do formy i pozostawienie do stwardnienia bez ciśnienia
b: do wytworzenia w tym procesie

Zatem przed rzutowaniem „obiekt” (nie dosłownie obiekt OOP) ma określony kształt (typ). Kiedy ponownie go rzucisz, czyli „wylejesz beton” wokół niego, aby nadać mu nowy kształt, to właśnie robisz z odlewem. Masz liczbę jako liczbę całkowitą w kształcie sześciokąta, a po rzuceniu otrzymasz ciąg w kształcie prostokąta.

Juha Untinen
źródło
2
Ponadto : „Aby przypisać określoną rolę (aktorowi)”.
Kelly Thomas
Tak. Jestem pewien, że to jest najlepsze. +1.
david.pfx
2

Przydatne może być rozdzielenie rzutów C na dwie grupy:

  1. Rzutowanie numeryczne - konwertuj liczbę między jedną reprezentacją na drugą, próbując zachować wartość. Na przykład - (int)3.1byłoby 3. Istnieją dokładne reguły określające, co dzieje się, gdy nie można zachować dokładnej wartości.

  2. Rzutowanie wskaźnika - Zachowaj adres pamięci, ale zmień sposób, w jaki jest wyrenderowany. Na przykład for float x=3.5, *(int *)&xdaje 1080033280- ta liczba całkowita jest reprezentowana przez ten sam wzór bitowy, który reprezentuje liczbę zmiennoprzecinkową 3.5.

ugoren
źródło
Keep the memory address, but change the way it's dereferenced.Dereferencje wskaźnika pisanego czcionką nie są zdefiniowane. Standard gwarantuje tylko rzutowanie z miejsca A *na miejsce B *i z powrotem, co spowoduje, że będzie to samo A *, co może nie być ważne dla dereferencji na pierwszym miejscu - lub, jeśli B *jest char *, może być użyte do odczytania reprezentacji obiektu dowolnego typu. W przypadku wszystkich innych typów B *wskaźnik dereferencji oznacza pisanie na klawiaturze, UB i narusza ścisłe aliasing. W każdym razie, nawet jeśli z tego powodu kompilator nie zniszczył powyższego przykładu 2, przyjmujesz nieportowalne założenia dotyczące wzorów bitowych
underscore_d
1
cast (v): to receive form in a mold

W C ++ różne rodzaje rzutowań mogą być bardziej wyraźne, reinterpret_castco oznacza „traktuj te bajty tak, jakby były już inną rzeczą”. W C można to absolutnie jednoznacznie określić za pomocą a union, rzutowanie z (type)operatorem będzie próbowało zachować wynik liczbowo równoważny, aż do utraty precyzji.

U2EF1
źródło
2
W języku C wskaźniki rzutowania są zawsze ponownie interpretowane, a rzutowania wartości zawsze zachowują wartość w najlepszy możliwy sposób. W C ++ istnieje wiele sposobów na rzutowanie wskaźnika, dlatego istnieją bardziej wyraźne typy rzutowania.
Jan Hudec
Semantyka rzutowana na C nie musi koniecznie „reinterpretować”. Byłoby uzasadnione, aby procesor, który używał adresowania słów, ale chciał ładnie współdziałać z kodem bajtowym int*, miał jedno słowo i char*dwa słowa [drugi bajt wybiera najwyższy lub najniższy bajt słowa]. Rzutowanie (int*)na (char*)wymagałoby dodania dodatkowego słowa, które powinno być dowolną wartością określającą pierwszy bajt int.
supercat