Konwersja typu - Type conversion

W informatyce , typu konwersji , typu odlewania , rodzaj przymusu i typu żonglerki różne sposoby zmienia się wyraz z jednego typu danych na inny. Przykładem może być konwersja wartości całkowitej na wartość zmiennoprzecinkową lub jej tekstową reprezentację jako ciąg znaków i na odwrót. Konwersje typów mogą korzystać z pewnych funkcji hierarchii typów lub reprezentacji danych . Dwa ważne aspekty konwersji typu to, czy odbywa się to niejawnie (automatycznie), czy jawnie oraz czy podstawowa reprezentacja danych jest konwertowana z jednej reprezentacji na inną, czy dana reprezentacja jest jedynie reinterpretowana jako reprezentacja innego typu danych. Ogólnie rzecz biorąc, można konwertować zarówno pierwotne , jak i złożone typy danych .

Każdy język programowania ma własne zasady dotyczące konwersji typów. Języki o silnym typowaniu zazwyczaj wykonują niewielką niejawną konwersję i zniechęcają do ponownej interpretacji reprezentacji, podczas gdy języki o słabym typowaniu wykonują wiele niejawnych konwersji między typami danych. Słaby język pisania często pozwala zmusić kompilator do arbitralnej interpretacji elementu danych jako posiadającego różne reprezentacje — może to być nieoczywisty błąd programistyczny lub techniczna metoda bezpośredniego radzenia sobie z podstawowym sprzętem.

W większości języków słowo przymus jest używane do oznaczenia niejawnej konwersji podczas kompilacji lub w czasie wykonywania . Na przykład w wyrażeniu mieszającym liczby całkowite i zmiennoprzecinkowe (takie jak 5 + 0,1) kompilator automatycznie przekonwertuje reprezentację liczb całkowitych na reprezentację zmiennoprzecinkową, aby ułamki nie zostały utracone. Jawne konwersje typów są albo wskazywane przez napisanie dodatkowego kodu (np. dodanie identyfikatorów typów lub wywołanie wbudowanych procedur ) albo przez zakodowanie procedur konwersji dla kompilatora, który ma zostać użyty, gdy w przeciwnym razie zatrzymałby się z powodu niezgodności typu.

W większości języków podobnych do ALGOL , takich jak Pascal , Modula-2 , Ada i Delphi , konwersja i rzutowanie to wyraźnie różne koncepcje. W tych językach konwersja odnosi się do pośredniej lub jawnej zmiany wartości z jednego formatu przechowywania typu danych na inny, np. 16-bitowej liczby całkowitej na 32-bitową liczbę całkowitą. Potrzeby w zakresie przechowywania mogą ulec zmianie w wyniku konwersji, w tym możliwej utraty precyzji lub obcięcia. Słowo obsada , z drugiej strony, odnosi się wyraźnie zmieniając interpretację tego wzorca bitowego reprezentującego wartość z jednego typu na inny. Na przykład 32 ciągłe bity mogą być traktowane jako tablica 32 wartości logicznych, 4-bajtowy ciąg, 32-bitowa liczba całkowita bez znaku lub wartość zmiennoprzecinkowa IEEE pojedynczej precyzji. Ponieważ przechowywane bity nigdy nie są zmieniane, programista musi znać szczegóły niskiego poziomu, takie jak format reprezentacji, kolejność bajtów i potrzeby wyrównania, aby uzyskać sensowne rzutowanie.

W rodzinie języków C i ALGOL 68 , słowo rzutowanie zazwyczaj odnosi się do jawnej konwersji typu (w przeciwieństwie do niejawnej konwersji), powodując pewną niejasność co do tego, czy jest to reinterpretacja wzorca bitowego, czy reprezentacja danych rzeczywistych konwersja. Ważniejsza jest mnogość sposobów i reguł, które mają zastosowanie do tego, jaki typ danych (lub klasa) jest lokalizowany przez wskaźnik i jak wskaźnik może być dostosowywany przez kompilator w przypadkach takich jak dziedziczenie obiektów (klas).

Porównanie językowe

Języki podobne do C

Niejawna konwersja typu

Niejawna konwersja typu, znana również jako coercion , to automatyczna konwersja typu przez kompilator . Niektóre języki programowania umożliwiają kompilatorom zapewnienie przymusu; inni tego wymagają.

W wyrażeniu typu mieszanego dane jednego lub większej liczby podtypów można w razie potrzeby przekonwertować na nadtyp w czasie wykonywania, aby program działał poprawnie. Na przykład poniższy kod jest legalnym kodem języka C :

double  d;
long    l;
int     i;

if (d > i)   d = i;
if (i > l)   l = i;
if (d == l)  d *= 2;

Chociaż d , l i i należą do różnych typów danych, zostaną one automatycznie przekonwertowane na równe typy danych za każdym razem, gdy wykonywane jest porównanie lub przypisanie. Takie zachowanie należy stosować ostrożnie, ponieważ mogą wystąpić niezamierzone konsekwencje . Dane mogą zostać utracone podczas konwertowania reprezentacji z liczb zmiennoprzecinkowych na liczby całkowite, ponieważ ułamkowe składniki wartości zmiennoprzecinkowych zostaną obcięte (zaokrąglone do zera). I odwrotnie, precyzja może zostać utracona podczas konwertowania reprezentacji z liczb całkowitych na zmiennoprzecinkowe, ponieważ typ zmiennoprzecinkowy może nie być w stanie dokładnie reprezentować typu całkowitego. Na przykład floatmoże to być typ o pojedynczej precyzji IEEE 754 , który nie może dokładnie reprezentować liczby całkowitej 16777217, podczas gdy 32-bitowy typ liczby całkowitej może. Może to prowadzić do nieintuicyjnego zachowania, jak pokazano w następującym kodzie:

#include <stdio.h>

int main(void)
{
    int i_value   = 16777217;
    float f_value = 16777216.0;
    printf("The integer is: %d\n", i_value);
    printf("The float is:   %f\n", f_value);
    printf("Their equality: %d\n", i_value == f_value);
}

W kompilatorach, które implementują floaty o pojedynczej precyzji IEEE, a ints jako co najmniej 32 bity, ten kod da następujący osobliwy wydruk:

The integer is: 16777217
The float is: 16777216.000000
Their equality: 1

Zauważ, że 1 reprezentuje równość w ostatnim wierszu powyżej. To dziwne zachowanie jest spowodowane niejawną konwersją i_valuena float podczas porównywania z f_value. Konwersja powoduje utratę precyzji, co powoduje wyrównanie wartości przed porównaniem.

Ważne dania na wynos:

  1. floatdo intprzyczyny skrócenia , czyli usunięcie części ułamkowej.
  2. doubledo floatprzyczyn zaokrąglania cyfry.
  3. longz intprzyczyn spada nadmiarowych bitów wyższych rzędów.
Wpisz promocję

Jednym szczególnym przypadkiem niejawnej konwersji typu jest promocja typu, w której kompilator automatycznie rozszerza binarną reprezentację obiektów typów całkowitych lub zmiennoprzecinkowych. Promocje są często używane z typami mniejszymi niż typ natywny jednostki arytmetyczno-logicznej (ALU) platformy docelowej , przed operacjami arytmetycznymi i logicznymi, aby umożliwić takie operacje lub bardziej wydajnie, jeśli jednostka ALU może pracować z więcej niż jednym typem. C i C++ przeprowadzają taką promocję dla obiektów typu logicznego, znakowego, szerokiego znaku, wyliczenia i krótkich typów całkowitych, które są promowane do int oraz dla obiektów typu float, które są promowane do double. W przeciwieństwie do niektórych innych konwersji typów, promocje nigdy nie tracą precyzji ani nie modyfikują wartości przechowywanej w obiekcie.

W Javie :

int x = 3;
double y = 3.5;
System.out.println(x + y); // The output will be 6.5

Jawna konwersja typu

Jawna konwersja typu to konwersja typu, która jest jawnie zdefiniowana w programie (zamiast być wykonywana przez kompilator w przypadku niejawnej konwersji typu). Jest definiowany przez użytkownika w programie.

double da = 3.3;
double db = 3.3;
double dc = 3.4;
int result = (int)da + (int)db + (int)dc; // result == 9
// if implicit conversion would be used (as with "result = da + db + dc"), result would be equal to 10

Istnieje kilka rodzajów jawnej konwersji.

w kratę
Przed wykonaniem konwersji wykonywane jest sprawdzenie w czasie wykonywania, aby sprawdzić, czy typ docelowy może przechowywać wartość źródłową. Jeśli nie, zgłaszany jest warunek błędu.
niepowstrzymany
Kontrola nie jest wykonywana. Jeśli typ docelowy nie może przechowywać wartości źródłowej, wynik jest niezdefiniowany.
wzór bitowy
Reprezentacja źródła w postaci surowego bitu jest dosłownie kopiowana i ponownie interpretowana zgodnie z typem docelowym. Można to również osiągnąć poprzez aliasowanie .

W obiektowych językach programowania obiekty mogą być również  rzutowane w dół : odwołanie do klasy bazowej jest rzutowane na jedną z jej klas pochodnych.

C# i C++

W języku C# konwersję typu można wykonać w sposób bezpieczny lub niebezpieczny (tj. podobny do języka C), poprzednio nazywany check type cast .

Animal animal = new Cat();

Bulldog b = (Bulldog) animal;  // if (animal is Bulldog), stat.type(animal) is Bulldog, else an exception
b = animal as Bulldog;         // if (animal is Bulldog), b = (Bulldog) animal, else b = null

animal = null;
b = animal as Bulldog;         // b == null

W C++ podobny efekt można osiągnąć za pomocą składni rzutowania w stylu C++ .

Animal* animal = new Cat;

Bulldog* b = static_cast<Bulldog*>(animal); // compiles only if either Animal or Bulldog is derived from the other (or same)
b = dynamic_cast<Bulldog*>(animal);         // if (animal is Bulldog), b = (Bulldog*) animal, else b = nullptr

Bulldog& br = static_cast<Bulldog&>(*animal); // same as above, but an exception will be thrown if a nullptr was to be returned
                                              // this is not seen in code where exception handling is avoided
animal = nullptr;
b = dynamic_cast<Bulldog*>(animal);         // b == nullptr

delete animal; // always free resources

Eiffla

W Eiffla pojęcie konwersji typów jest zintegrowane z regułami systemu typów. Reguła przypisania mówi, że przydział, taki jak:

x := y

jest prawidłowy wtedy i tylko wtedy, gdy typ jego wyrażenia źródłowego, yw tym przypadku, jest zgodny z typem jego jednostki docelowej, xw tym przypadku. W tej regule zgodny z oznacza, że ​​typ wyrażenia źródłowego jest zgodny lub konwertowany na typ docelowy. Zgodność typów jest definiowana przez znane zasady polimorfizmu w programowaniu obiektowym . Na przykład, w powyższym przypisaniu, typ yjest zgodny z typem, xjeśli klasa, na której ysię opiera, jest potomkiem tej, na której xsię opiera.

Definicja konwersji typu w Eiffel

Działania konwersji typu w systemie Eiffel, konkretnie konwertujące na i konwertujące z, są definiowane jako:

Typ oparty na klasie CU przekształca się w typ T oparty na klasie CT (a T konwertuje z U), jeśli

CT ma procedurę konwersji wykorzystującą U jako typ konwersji lub
CU ma zapytanie o konwersję z listą T jako typem konwersji

Przykład

Eiffel jest w pełni zgodnym językiem dla Microsoft .NET Framework . Przed rozwojem platformy .NET Eiffel posiadał już rozbudowane biblioteki klas. Korzystanie z bibliotek typów .NET, szczególnie w przypadku często używanych typów, takich jak ciągi, stwarza problem z konwersją. Istniejące oprogramowanie Eiffla używa klas ciągów (takich jak STRING_8) z bibliotek Eiffla, ale oprogramowanie Eiffla napisane dla .NET musi System.Stringw wielu przypadkach używać klasy ciągów .NET ( ), na przykład podczas wywoływania metod .NET, które oczekują elementów .NET typ do przekazania jako argumenty. Tak więc konwersja tych typów tam iz powrotem musi być tak płynna, jak to tylko możliwe.

    my_string: STRING_8                 -- Native Eiffel string
    my_system_string: SYSTEM_STRING     -- Native .NET string

        ...

            my_string := my_system_string

W powyższym kodzie zadeklarowane są dwa ciągi, po jednym każdego innego typu ( SYSTEM_STRINGjest to zgodny z Eiffel alias dla System.String). Ponieważ System.Stringnie jest zgodny z STRING_8, powyższe przypisanie jest ważne tylko w przypadku System.Stringkonwersji na STRING_8.

Klasa Eiffla STRING_8posiada procedurę konwersji make_from_cildla obiektów typu System.String. Procedury konwersji są również zawsze oznaczane jako procedury tworzenia (podobnie jak konstruktory). Oto fragment z STRING_8zajęć:

    class STRING_8
        ...
    create
        make_from_cil
        ...
    convert
        make_from_cil ({SYSTEM_STRING})
        ...

Obecność procedury konwersji sprawia, że ​​cesja:

            my_string := my_system_string

semantycznie równoważne z:

            create my_string.make_from_cil (my_system_string)

w którym my_stringjest skonstruowany jako nowy obiekt typu STRING_8o treści równoważnej do my_system_string.

Aby obsłużyć przypisanie z odwróconym oryginalnym źródłem i celem:

            my_system_string := my_string

klasa STRING_8zawiera również zapytanie konwersyjne, to_cilktóre wygeneruje a System.Stringz instancji STRING_8.

    class STRING_8
        ...
    create
        make_from_cil
        ...
    convert
        make_from_cil ({SYSTEM_STRING})
        to_cil: {SYSTEM_STRING}
        ...

Przydzial:

            my_system_string := my_string

wtedy staje się równoznaczne z:

            my_system_string := my_string.to_cil

W systemie Eiffel konfiguracja konwersji typu jest zawarta w kodzie klasy, ale wydaje się, że zachodzi tak samo automatycznie, jak jawna konwersja typu w kodzie klienta. Obejmuje to nie tylko przypisania, ale także inne rodzaje załączników, takie jak podstawianie argumentów (parametrów).

Rdza

Rust nie zapewnia niejawnej konwersji typu (przymusu) między typami pierwotnymi. Ale jawną konwersję typu (rzutowanie) można wykonać za pomocą assłowa kluczowego.

println!("1000 as a u16 is: {}", 1000 as u16);

Problemy z bezpieczeństwem

W hackingu rzutowanie typu to niewłaściwe użycie konwersji typu do tymczasowej zmiany typu danych zmiennej w stosunku do tego, jak był pierwotnie zdefiniowany. Daje to możliwości hakerom, ponieważ podczas konwersji typu po „przerzuceniu typu” zmiennej na inny typ danych, kompilator potraktuje tę zhakowaną zmienną jako nowy typ danych dla tej konkretnej operacji.

Zobacz też

Bibliografia

Linki zewnętrzne