W artykule opisano główne kroki, jakie należy podjąć transfer programów z wersji 32-bitowej na 64-bitową. Opisano główne problemy, z jakimi borykają się programiści planujący migrację programów 32-bitowych do systemów 64-bitowych. Oczywiście lista poruszonych kwestii nie jest kompletna, ale mamy nadzieję, że w przyszłości udostępnimy bardziej szczegółową wersję tego artykułu.
Przenieś programy 32-bitowe do wersji 64-bitowej
Oto kroki, które należy wykonać, aby pomyślnie migrować aplikacje z Windows od systemów Windows 32-bitowych do 64-bitowych:
Krok 1: Tryb 64-bitowy może być inny. Naprawmy to
W kontekście architektury komputera termin „64-bitowy” odnosi się do 64-bitowych liczb całkowitych i innych typów danych tej wielkości. Systemy „64-bitowe” mogą oznaczać 64-bitowe architektury mikroprocesorów (np. EM64T, IA-64) lub 64-bitowe architektury systemów operacyjnych (np. Windows XP Professional x64 Edition).
AMD64 (lub x86-64, Intel 64, EM64T, x64) to 64-bitowa architektura mikroprocesora i odpowiadający jej zestaw instrukcji opracowany przez firmę AMD. Ten zestaw instrukcji został licencjonowany przez firmę Intel pod nazwą EM64T (Intel64). Architektura AMD64 jest rozwinięciem architektury x86 z pełną kompatybilnością wsteczną.
Architektura ta stała się powszechna jako podstawa komputerów osobistych i stacji roboczych. IA-64 to 64-bitowa architektura mikroprocesora opracowana wspólnie przez firmy Intel i Hewlett Packard. Jest zaimplementowany na mikroprocesorach Itanium i Itanium 2. Architektura jest stosowana głównie w serwerach wieloprocesorowych i systemach klastrowych.
Może być zainteresowany: Jak pobrać urządzenie PCI dla systemu Windows 7 64-bitowego
AMD64 i IA-64 to dwie różne architektury 64-bitowe, które są ze sobą niekompatybilne. Dlatego deweloperzy muszą od razu podjąć decyzję, czy chcą wspierać obie architektury, czy tylko jedną. W większości przypadków, jeśli nie tworzysz wysoce dostosowanego oprogramowania dla systemów klastrowych lub nie wdrażasz własnego wydajnego systemu DBMS, najprawdopodobniej będziesz musiał wdrożyć obsługę tylko dla architektury AMD64, która jest znacznie bardziej popularna niż IA-64.
Jest to zwłaszcza oprogramowanie przeznaczone na rynek PC, który niemal w 100% jest zajęty architekturą AMD64. W dalszej części artykułu porozmawiamy tylko o architekturze AMD64 (EM64T, x64), ponieważ jest ona obecnie najbardziej aktualna dla twórców oprogramowania aplikacyjnego. Mówiąc o różnych architekturach, nie sposób nie wspomnieć o pojęciu „model danych”.
przez model danych Rozumiemy zależności pomiędzy akceptowanymi rozmiarami czcionek w środowisku programistycznym. Może istnieć wiele narzędzi programistycznych obsługujących różne typy danych dla danego systemu operacyjnego. Ale zazwyczaj dominuje tylko jeden model, który bardziej odpowiada środowisku sprzęt komputerowy y oprogramowanie.
Przykładem tego jest 64-bitowy system Windows, którego oryginalny model danych to LLP64. Jednak ze względu na kompatybilność 64-bitowy system Windows obsługuje uruchamianie 32-bitowych programów działających w trybie modelu danych ILP32LL. Tabela 1 zawiera informacje na temat podstawowych modeli danych.
Zastosowany model danych ma ogromny wpływ na proces tworzenia aplikacji 64-bitowych, ponieważ należy wziąć pod uwagę rozmiar danych wykorzystywanych w kodzie programu.
Krok 2: Dowiedz się, czy potrzebujesz 64-bitowej wersji swojego produktu
Opanowywanie systemów 64-bitowych należy rozpocząć od zadania sobie pytania: „Czy naprawdę muszę przebudować swój projekt pod kątem systemu 64-bitowego?” Odpowiedź na to pytanie możesz dać dopiero po głębokim przemyśleniu sprawy. Z jednej strony, możesz zostać w tyle za konkurencją, jeśli nie zaoferujesz rozwiązań 64-bitowych. Z drugiej strony możesz stracić El Tiempo tworzenie aplikacji 64-bitowej, która nie zapewni żadnej przewagi konkurencyjnej. Wymieńmy podstawowe czynniki, które pomogą Ci podjąć decyzję.
2.1. Długość cyklu życia aplikacji
Nie należy tworzyć 64-bitowej wersji aplikacji o krótkim cyklu życia. Dzięki podsystemowi WOW64 stare aplikacje 32-bitowe działają całkiem dobrze na 64-bitowych systemach Windows, dlatego nie ma sensu tworzyć programu 64-bitowego, ponieważ za 2 lata nie będzie on wspierany.
Ponadto praktyka pokazuje, że migracja do 64-bitowych wersji systemu Windows opóźniła się i być może większość jego użytkowników w najbliższej perspektywie będzie korzystać wyłącznie z 32-bitowej wersji rozwiązania programu.
Jeśli planujesz długoterminowy rozwój i wsparcie produktu programowego, powinieneś rozpocząć pracę z 64-bitową wersją swojego rozwiązania. Możesz to zrobić bez pośpiechu, jednak pamiętaj, że im dłużej nie posiadasz pełnej wersji 64-bitowej, tym większe będziesz miał trudności z obsługą tej aplikacji zainstalowanej na 64-bitowych wersjach systemu Windows.
2.2. Intensywne wykorzystanie zasobów aplikacji
Rekompilacja programu dla systemu 64-bitowego pozwoli na wykorzystanie dużych rozmiarów pamięci głównej, a także przyspieszy jej działanie o 5-15%. Wzrost o 5-10% zostanie uzyskany dzięki wykorzystaniu możliwości architektury 64-bitowego procesora, np. większej liczby rejestrów. Pozostałą część wzrostu prędkości o 1–5% można wytłumaczyć brakiem warstwy WOW64, która tłumaczy wywołania API między aplikacjami 32-bitowymi a 64-bitowym systemem operacyjnym.
Jeśli Twój program nie obsługuje dużych danych (więcej niż 2 GB) i szybkość jego działania nie jest kluczowa, to migracja na system 64-bitowy W najbliższej przyszłości nie będzie to już tak pilne. Nawiasem mówiąc, nawet proste aplikacje 32-bitowe mogą zyskać na uruchomieniu w środowisku 64-bitowym.
Być może wiesz, że program utworzony za pomocą klawisza / DUŻE OPROGRAMOWANIE ADRESOWE: TAK Możesz przydzielić do 3 GB pamięci, jeśli 32-bitowy system Windows zostanie uruchomiony za pomocą klucza. Ten 32-bitowy program uruchomiony na systemie 64-bitowym może przydzielić prawie 4 GB pamięci (w praktyce około 3,5 GB).
2.3. Rozwój biblioteki
Jeśli tworzysz biblioteki, komponenty lub inne elementy przy pomocy zewnętrznych programistów, którzy tworzą własne oprogramowanie, musisz działać szybko podczas tworzenia 64-bitowej wersji swojego produktu. W przeciwnym razie klienci zainteresowani uruchomieniem wersji 64-bitowych będą musieli szukać alternatywnych rozwiązań.
Na przykład niektórzy twórcy zabezpieczeń oprogramowania i sprzętu zareagowali powoli, wypuszczając programy 64-bitowe, co spowodowało, że niektórzy klienci zaczęli szukać innych narzędzi do ochrony swoich programów.
Dodatkową zaletą wydania 64-bitowej wersji biblioteki jest to, że możesz go sprzedać jako osobny produkt. Dlatego Twoi klienci, którzy chcą tworzyć aplikacje zarówno 32-bitowe, jak i 64-bitowe, będą musieli zakupić 2 różne licencje.
2.4. Zależność Twojego produktu od bibliotek stron trzecich
Zanim zaplanujesz prace nad stworzeniem 64-bitowej wersji swojego produktu, dowiedz się, czy używane są 64-bitowe wersje bibliotek i komponentów. Oprócz tego zapoznaj się z polityką cenową 64-bitowej wersji biblioteki. Jeśli wsparcie nie jest zapewnione, poszukaj wcześniej alternatywnych rozwiązań obsługujących systemy 64-bitowe.
2.5. Korzystanie z aplikacji 16-bitowych
Jeśli Twoje rozwiązania nadal korzystają z dysków 16-bitowych, czas się ich pozbyć. Aplikacje 16-bitowe w 64-bitowych wersjach systemu Windows nie są obsługiwane. Powinniśmy wyjaśnić tutaj jedną rzecz dotyczącą używania 16-bitowych instalatorów. Nadal są używane do instalowania niektórych aplikacji 32-bitowych.
Istnieje specjalny mechanizm, który zastępuje niektóre z najpopularniejszych 16-bitowych instalatorów ich nowszymi wersjami. Może to prowadzić do fałszywe przekonanie, że programy 16-bitowe nadal działają w środowisku 64-bitowym. Pamiętaj: to nie tak.
2.6. Montaż kodu
Nie zapominaj, że użycie dużego kodu asemblera może znacznie zwiększyć koszt stworzenia 64-bitowej wersji aplikacji. Po rozważeniu wszystkich wymienionych czynników i rozważeniu wszystkich za i przeciw, zdecyduj, czy musisz przenieść swój projekt na systemy 64-bitowe. Jeśli odpowiedź brzmi tak, możemy pójść dalej.
Krok 3: Zestaw narzędzi
Jeśli zdecydowałeś się opracować 64-bitową wersję swojego produktu i jesteś gotowy poświęcić czas, to nadal nie wystarczy, aby zagwarantować sukces. Rzecz w tym, że musisz posiadać cały niezbędny zestaw narzędzi i tutaj możesz napotkać pewne trudności. Brak 64-bitowego kompilatora może być najprostszym, ale najbardziej nie do pokonania problemem.
Jeśli wszystko jest jasne co do braku 64-bitowego kompilatora, inne podobne problemy mogą wydawać się mniej przejrzyste i wystąpić dopiero na etapie przenoszenia projektu na nową architekturę. Dlatego też zalecamy wcześniejsze sprawdzenie, czy istnieją wszystkie niezbędne komponenty, które będą potrzebne do wdrożenia 64-bitowej wersji produktu. Możesz spotkać się z nieprzyjemnymi niespodziankami.
Oczywiście nie da się tutaj wymienić wszystkiego, czego możesz potrzebować do projektu, ale będziemy kontynuować listę, aby pomóc Ci się zorientować i być może przypomnieć sobie inne rzeczy niezbędne do zaimplementuj swój projekt 64-bitowy:
3.1. Kompilator 64-bitowy
Nie ma nic więcej do powiedzenia na temat znaczenia posiadania 64-bitowego kompilatora. Po prostu tak musi być. Jeśli planujesz tworzyć aplikacje 64-bitowe przy użyciu najnowszej wersji programu Visual Studio, poniższa tabela 2 pomoże Ci zrozumieć, jakiej wersji programu Visual Studio potrzebujesz.

3.2. Komputery 64-bitowe pod kontrolą 64-bitowego systemu operacyjnego
Oczywiście, możesz użyć maszyny wirtualne aby uruchomić aplikacje 64-bitowe na komputerach 32-bitowych, ale jest to zbyt niewygodne i nie zapewnia wymaganego poziomu testowania. Pożądane jest, aby maszyny miały nie mniej niż 4-8 GB pamięci głównej.
3.3. 64-bitowe wersje wszystkich używanych bibliotek
Jeśli biblioteki są prezentowane w kodach źródłowych, musi istnieć 64-bitowa konfiguracja projektu. Samodzielna aktualizacja biblioteki dla systemu 64-bitowego może być trudnym i niewdzięcznym zadaniem, a wynik może być zawodny i zawierać błędy. Ponadto tymi działaniami możesz naruszyć umowy licencyjne. Jeśli korzystasz z bibliotek w postaci dysków binarnych, powinieneś dowiedzieć się również, czy istnieją dyski 64-bitowe.
Nie można używać 32-bitowej biblioteki DLL w aplikacji 64-bitowej. Możesz utworzyć specjalne łącze przez COM, ale będzie to osobne, duże i trudne zadanie. Należy również pamiętać, że zakup 64-bitowej wersji biblioteki może wymagać wydania dodatkowych pieniędzy.
3.4. Brak zintegrowanego kodu asemblera
Visual C++ nie obsługuje 64-bitowego asemblera wbudowanego. Musisz użyć zewnętrznego 64-bitowego asemblera (np. MASM) lub mieć implementację o tej samej funkcjonalności w C/C++.
3.5. Aktualizacja metodologii testów
Oznacza to znaczną przebudowę metodyki testowania, aktualizację testów jednostkowych i zastosowanie nowych narzędzi. Porozmawiamy o tym szerzej później, ale nie zapomnij wziąć tego pod uwagę na etapie oceny kosztów czasowych migracji aplikacji na nowy system.
3.6. Nowe dane do przetestowania
Jeśli tworzysz aplikacje wymagające dużej ilości zasobów i korzystające z dużej ilości pamięci głównej, powinieneś zapewnić uzupełnienie testowej bazy danych wejściowych. Podczas testowania obciążenia aplikacji 64-bitowych dobrym pomysłem jest przekroczenie limitu 4 GB używanej pamięci. Wiele błędów może wystąpić tylko w tych warunkach.
3.7. 64-bitowe systemy bezpieczeństwa
Zastosowany system zabezpieczeń musi zapewniać pełną kompatybilność z systemami 64-bitowymi. Już dawno nie było systemu automatycznej ochrony 64-bitowych plików binarnych (program Hasp Envelop).
W związku z tym konieczne było ręczne zaimplementowanie mechanizmu bezpieczeństwa w kodzie programu, co było zadaniem trudniejszym, wymagającym profesjonalizmu i czasu. Nie zapomnij o kwestiach związanych m.in. z bezpieczeństwem, aktualizacjami systemu.
3.8. Instalator
Potrzebujesz nowego instalatora, który będzie w stanie w pełni zainstalować aplikacje 64-bitowe. Chcielibyśmy ostrzec Cię o bardzo typowym błędzie. Jest to tworzenie instalatorów 64-bitowych w celu instalowania produktów programów 32/64-bitowych. Przygotowując 64-bitową wersję aplikacji, programiści często chcą, aby „tryb 64-bitowy” był absolutny i tworzył 64-bitowy instalator, zapominając, że osoby korzystające z 32-bitowego systemu operacyjnego po prostu nie mogą uruchomić takiego pakietu instalacyjnego.
Zwróć uwagę, że nie jest to aplikacja 32-bitowa znajdująca się w pakiecie dystrybucyjnym wraz z wersją 64-bitową, ale sam instalator. Bo jeśli pakiet dystrybucyjny jest aplikacją 64-bitową, to oczywiście nie będzie działać na 32-bitowym systemie operacyjnym. Najbardziej nieprzyjemne jest to, że użytkownik nie będzie w stanie odgadnąć, dlaczego tak się dzieje.
Krok 4: Konfiguracja projektu w Visual Studio 2005/2008
Tworzenie 64-bitowej konfiguracji projektu w Visual Studio wydaje się dość proste. Trudności zaczną się już na etapie budowania nowej konfiguracji i szukania w niej błędów. Aby utworzyć samą konfigurację 64-bitową, należy wykonać następujące 4 kroki:
Krok 1: Uruchom menedżera konfiguracji, jak pokazano na obrazku 1:

Krok 2: W menedżerze konfiguracji wybierz obsługę nowej platformy:

Krok 3: wybierz platformę 64-bitową (x64) i jako bazę konfigurację wersji 32-bitowej. Program Visual Studio automatycznie poprawi ustawienia mające wpływ na tryb kompilacji.

Krok 4: Dodawanie nowej konfiguracji zostało zakończone i możesz teraz wybrać 64-bitową wersję konfiguracji i rozpocząć tworzenie 64-bitowej aplikacji. Wybór konfiguracji 64-bitowej dla kompilacji pokazano na obrazku 4 4.

Jeśli będziesz mieć szczęście, nie będziesz musiał dodatkowo konfigurować projektu 64-bitowego. Ale w dużej mierze zależy to od projektu, jego złożoności i liczby wykorzystanych bibliotek. Jedyne, co musisz zmienić od razu, to rozmiar stosu. Jeśli rozmiar sterty w Twoim projekcie jest ustawiony domyślnie na 1 MB, musisz zdefiniować go jako 2 MB dla wersji 64-bitowej.
Nie jest to konieczne, ale lepiej się wcześniej upewnić. Jeśli używasz innego rozmiaru niż domyślny, warto zwiększyć go dwukrotnie dla wersji 64-bitowej. Aby to zrobić, znajdź i zmień parametry Rozmiar rezerwy stosu y Rozmiar zatwierdzenia stosu w ustawieniach projektu.
Krok 5: Zbuduj aplikację
W tym miejscu powinniśmy opowiedzieć o typowych problemach występujących na etapie kompilacji konfiguracji 64-bitowej, omówić jakie problemy pojawiają się w bibliotekach innych firm, poinformować, że w kodzie związanym z funkcjami WinAPI kompilator nie pozwoli na umieszczenie wskaźnika wpisz LONG i będziesz musiał zaktualizować swój kod i użyć typu LONG_PTG. A jest o wiele więcej do powiedzenia.
Niestety problemów jest tak wiele, a błędy są tak różne, że nie jesteśmy w stanie opisać ich wszystkich w jednym artykule, ani nawet w jednej książce. Będziesz musiał przejrzeć wszystkie błędy wyświetlane przez kompilator i wszystkie nowe ostrzeżenia, których wcześniej nie było, i w każdym konkretnym przypadku dowiedzieć się, jak zaktualizować kod.
Opiszmy tutaj jedynie typy, które mogą zainteresować programistów podczas przenoszenia aplikacji. Większość błędów rekompilacji będzie dotyczyć używania tych samych typów:
- W t 32/32: typ podstawowy. W systemach 64-bitowych nadal jest to wersja 32-bitowa.
- Długie 32 / 32: typ podstawowy. W 64-bitowych systemach Windows jest to nadal wersja 32-bitowa. Należy pamiętać, że w systemach Linux 64-bitowy, ten typ został rozszerzony do wersji 64-bitowej. Nie zapomnij o tym, jeśli tworzysz kod, który powinien zostać skompilowany dla systemów Windows i Linux.
- rozmiar_t 32 / 64: niepodpisany typ podstawowy. Rozmiar typu dobiera się tak, aby można było do niego zapisać maksymalny, teoretycznie możliwy rozmiar tablicy. Możesz bezpiecznie umieścić wskaźnik do typu size_t (z wyjątkiem wskaźników do funkcji klasowych, ale jest to szczególny przypadek).
- ptrdiff_t 32 / 64: podobny do typu size_t, ale jest to typ ze znakiem. Wynik wyrażenia, w którym jeden wskaźnik jest odejmowany od drugiego (ptr1-ptr2), będzie miał typ ptrdiff_t.
- Wskaźnik 32/64: Rozmiar wskaźnika zależy bezpośrednio od wielkości platformy. Zachowaj ostrożność podczas konwertowania wskaźników na inne typy.
- __int64 64 / 64: 64-bitowy typ ze znakiem.
- DWORD 32/32: 32-bitowy typ bez znaku. W WinDef.h jest to zdefiniowane jako: typedef unsigned long DWORD;
- DWORDLONG 64/64: 64-bitowy typ bez znaku. W WinNT.h jest to zdefiniowane jako: typedef ULONGLONG DWORDLONG.
- DWORD_PTR 32/64: typ bez znaku, w którym można umieścić wskaźnik. W BaseTsd.h jest to zdefiniowane jako: typedef ULONG_PTR DWORD_PTR.
- DWORD32 32 / 32: 32-bitowy typ bez znaku. W BaseTsd.h jest to zdefiniowane jako: typedef unsigned int DWORD32.
- DWORD64 64 / 64: 64-bitowy typ bez znaku. W BaseTsd.h jest to zdefiniowane jako: typedef unsigned __int64 DWORD64.
- HALF_PTR 16 / 32: pół wskaźnika. W Basetsd.h jest to zdefiniowane jako: #ifdef _WIN64. typedef int HALF_PTR; #else wpiszdef short HALF_PTR; #endif
- INT_PTR 32 / 64: typ ze znakiem, do którego można umieścić wskaźnik. W BaseTsd.h jest to zdefiniowane jako: #if zdefiniowane (_WIN64) typedef __int64 INT_PTR; #else typedef int INT_PTR; #endif
- Długie 32 / 32: typ ze znakiem, który pozostał 32-bitowy. Dlatego w wielu przypadkach należy teraz używać LONG_PTR. W WinNT.h jest to zdefiniowane jako: typedef long LONG;
- LONG_PTR 32 / 64: typ ze znakiem, do którego można umieścić wskaźnik. W BaseTsd.h jest to zdefiniowane jako: #if zdefiniowane (_WIN64) typedef __int64 LONG_PTR; #else wpiszdef long LONG_PTR; #endif.
- LPARAM 32/64: parametr do wysyłania wiadomości. W WinNT.h jest to zdefiniowane jako: typedef LONG_PTR LPARAM.
- ROZMIAR_T 32 / 64: analog typu size_t. W BaseTsd.h jest to zdefiniowane jako: typedef ULONG_PTR SIZE_T.
- SSIZE_T 32/64: Analog typu ptrdiff_t. W BaseTsd.h jest to zdefiniowane jako: typedef LONG_PTR SSIZE_T.
- ULONG_PTR 32 / 64: typ bez znaku, w którym można umieścić wskaźnik. W BaseTsd.h jest to zdefiniowane jako: #if zdefiniowane (_WIN64) typedef unsigned __int64 ULONG_PTR; #else typedef unsigned long ULONG_PTR; #endif.
- WORD 16/16: Typ 16-bitowy bez znaku. W WinDef.h jest to zdefiniowane jako: typedef unsigned short WORD.
- WPARAM 32/64: parametr do wysyłania wiadomości. W WinDef.h jest to zdefiniowane jako: typedef UINT_PTR WPARAM.
Oto typy, które należy wziąć pod uwagę podczas migracji programów 32-bitowych do 64-bitowych systemów Windows.
Może chcesz wiedzieć: Jak wykorzystać całą pamięć RAM w systemie Windows 10
6. Diagnoza błędów ukrytych
Jeśli myślisz, że po naprawieniu wszystkich błędów kompilacji otrzymasz długo oczekiwaną aplikację 64-bitową, musimy Cię rozczarować. Najtrudniejsza część jeszcze przed nami. Na etapie kompilacji poprawi najbardziej oczywiste błędy, jakie udało się wykryć kompilatorowi, a które w większości są związane z niemożnością ukrytej konwersji typów.
Ale to tylko niewielka część problemu. Większość błędów jest ukryta. Z punktu widzenia abstrakcyjnego języka C++ błędy te wydają się bezpieczne i są maskowane przez jawne konwersje typów. Liczba takich błędów jest znacznie większa niż liczba błędów wykrytych na etapie kompilacji.
Nie należy pokładać nadziei w kluczu /Wp64. Klucz ten jest często przedstawiany jako wspaniały sposób wyszukiwania błędów 64-bitowych. W rzeczywistości klucz /Wp64 pozwala po prostu otrzymywać komunikaty ostrzegawcze dotyczące niepoprawności niektórych sekcji kodu w trybie 64-bitowym, podczas kompilacji kodu 32-bitowego.
Podczas kompilacji kodu 64-bitowego ostrzeżenia te będą nadal wyświetlane. I dlatego klucz /Wp64 jest ignorowany podczas budowania aplikacji 64-bitowej. I na pewno ten klucz nie pomoże w znalezieniu ukrytych błędów. Rozważmy kilka przykładów ukrytych błędów.
6.1. Jawna konwersja typu
Najprostszy rodzaj błędu (choć z pewnością nie najłatwiejszy do wykrycia) dotyczy konwersji typu jawnego, podczas których obcinane są znaczące bity. Popularnym przykładem jest konwersja wskaźników na typy 32-bitowe poprzez przekazanie ich do funkcji takich jak SendMessage:

W tym przypadku jawna konwersja typu służy do konwersji wskaźnika na typ numeryczny. W przypadku architektury 32-bitowej ten przykład jest poprawny, ponieważ ostatni parametr funkcji SendMessage ma typ LPARAM, który odpowiada DWORD w architekturze 32-bitowej. W przypadku architektury 64-bitowej wartość DWORD jest niepoprawna i należy ją zastąpić wartością LPARAM. Typ LPARAM ma rozmiar 32 lub 64 bitów, w zależności od architektury.
Jest to prosty przypadek, ale konwersja typów często wydaje się bardziej skomplikowana i niemożliwa do wykrycia za pomocą ostrzeżeń kompilatora lub przeszukiwania tekstu programu. Jawne konwersje typów powstrzymują diagnostykę kompilatora, ponieważ są przeznaczone właśnie do tego celu: poinformowania kompilatora, że konwersja typu jest poprawna i że programista jest odpowiedzialny za bezpieczeństwo kodu.
Wyszukiwanie jawne też nie pomoże. Typy mogą mieć niestandardowe nazwy (definiowane przez programistę poprzez typedef), a liczba metod umożliwiających jawną konwersję typów jest również duża. Aby bezpiecznie zdiagnozować te błędy, należy skorzystać ze specjalnego zestawu narzędzi, np. analizatorów Viva64 lub PC-Lint.
6.2. Niejawna konwersja typu
Poniższy przykład odnosi się do niejawnej konwersji typu, gdy utracone są również ważne bity. Kod funkcji fread odczytuje plik, ale jest niepoprawny przy próbie odczytania więcej niż 2 GB w systemie 64-bitowym.

Funkcja __fread zwraca typ size_t, ale typ int służy do przechowywania liczby odczytanych bajtów. W rezultacie przy dużych rozmiarach odczytywanych danych funkcja może zwrócić fałszywą liczbę bajtów. Można powiedzieć, że jest to kod niepiśmienny dla początkujących, że kompilator ogłosi tego typu konwersję i że kod ten jest naprawdę łatwy do znalezienia i naprawienia. Tak jest w teorii.
W praktyce przy dużych projektach wszystko może wyglądać zupełnie inaczej. Ten przykład pochodzi z kodu źródłowego FreeBSD. Błąd został naprawiony w grudniu 2008! Należy pamiętać, że pierwsza (eksperymentalna) 64-bitowa wersja FreeBSD została wydana w czerwcu 2003 roku.
6.3. Bity i zmiany
Pracując z oddzielnymi bitami, łatwo jest popełnić błąd w kodzie. Następny rodzaj błędów dotyczy operacji zmianowych. Oto przykład:

Kod ten działa dobrze na architekturze 32-bitowej i pozwala ustawić bity o liczbach od 0 do 31 do jedności. Po przeniesienie programu na platformę 64-bitową, będziesz musiał ustawić bity od 0 do 63. Ale ten kod nigdy nie ustawi bitów 32-63.
Zwróć uwagę, że „1” ma typ int, a gdy nastąpi zmiana w 32 pozycjach, nastąpi przepełnienie, jak pokazano na obrazku. To, czy w rezultacie otrzymamy 0 (rysunek B), czy 1 (rysunek C), zależy od implementacji kompilatora.

Aby naprawić kod, musimy ustawić stałą „1” tego samego typu co zmienna maski:
ptrdiff_t maska = ptrdiff_t(1) << bitNum;
Zwróć także uwagę na to, że zły kod prowadzi do jeszcze jednego błędu. W przypadku ustawienia 31 bitów w systemie 64-bitowym wynikiem funkcji będzie wartość 0xffffffff80000000. Wynikiem wyrażenia 1 << 31 jest liczba ujemna -2147483648. W 64-bitowej zmiennej całkowitej liczba ta jest prezentowana jako 0xffffffff80000000.

6.4. magiczne liczby
Magiczne stałe, czyli liczby za pomocą który określa rozmiar tego czy tamtego typu, może powodować wiele problemów. Właściwą decyzją byłoby użycie do tych celów operatorów sizeof(), ale w dużym programie sekcja starego kodu może nadal być ukryta tam, gdzie – jak uważają programiści – rozmiar wskaźnika wynosi 4 bajty, a w size_t jest to zawsze 32 bity. Zazwyczaj błędy te wyglądają następująco:

Poniżej znajdują się podstawowe liczby, na które należy uważać podczas przenoszenia programów z wersji 32-bitowej na 64-bitową.

Te z podstawowymi magicznymi wartościami są niebezpieczne podczas migracji aplikacji z platformy 32-bitowej na 64-bitową.
6.5. Błędy związane z używaniem zmiennych 32-bitowych jako indeksów
W programach przetwarzających duże dane mogą wystąpić błędy związane z indeksowaniem dużych tablic lub wiecznymi pętlami. Poniższy przykład zawiera 2 błędy:

Pierwszy błąd tutaj jest tak, że jeśli rozmiar przetwarzanych danych przekracza 4 GB (0xFFFFFFFF), może wystąpić wieczna pętla, ponieważ zmienna 'ja' ma typ „unsigned' i nigdy nie osiągnie wartości 0xFFFFFFFF. Celowo piszemy, że może się to zdarzyć, ale niekoniecznie. Zależy to od kodu, który zbuduje kompilator.
Na przykład w trybie debugowania będzie obecna wieczna pętla, a w kodzie wydania nie będzie pętli, ponieważ kompilator zdecyduje się zoptymalizować kod przy użyciu 64-bitowego rejestru dla licznika i pętla będzie poprawna. Wszystko to powoduje wiele zamieszania, a kod, który działał wczoraj, dzisiaj może zawieść.
Drugi błąd Wiąże się to z analizą macierz od początku do końca, aby określić, które ujemne wartości indeksu są używane. Ten kod będzie działał poprawnie w trybie 32-bitowym, ale po uruchomieniu na komputerze 64-bitowym, przy pierwszej iteracji pętli nastąpi przekroczenie dopuszczalnego dostępu do tablicy i program ulegnie awarii. Przeanalizujmy przyczynę takiego zachowania. Zgodnie z regulaminem C + +, wyrażenie „-i – jeden” w systemie 32-bitowym zostanie obliczone w następujący sposób: (w pierwszym kroku i = 0):
Wyrażenie „-i” ma typ bez znaku i wartość 0x00000000u.
Zmienna 'pierwszej'będzie rozszerzeniem typu'int' na typ bez znaku i będzie równy 0x00000001u. Uwaga: Typ int rozciąga się (zgodnie ze standardem C++) aż do typu 'unsigned' jeśli uczestniczysz w operacji, w której drugi argument ma typ bez znaku. Przeprowadzana jest operacja odejmowania, w której uczestniczą dwie wartości typu unsigned, a wynikiem operacji jest 0x00000000u – 0x00000001u = 0xFFFFFFFFu. Należy pamiętać, że wynik będzie typu bez znaku.
W systemie 32-bitowym dostęp do tablicy za pomocą indeksu 0xFFFFFFFFu jest taki sam, jak przy użyciu indeksu -1. To jest koniec [0xFFFFFFFFu], jest odpowiednikiem końca [-1]. Dzięki temu elementy tablicy zostaną poprawnie przetworzone. W systemie 64-bitowym sytuacja będzie zupełnie inna niż w poprzednim punkcie.
Typ unsigned zostanie rozszerzony do typu ptfdiff_t ze znakiem, a indeks tablicy będzie równy 0x00000000FFFFFFFFi64. W rezultacie nastąpi przepełnienie. Aby poprawić kod, musisz użyć typów ptrdiff_t i size_t.
6.6. Błędy związane ze zmianą rodzaju używanych funkcji
Są błędy, za które nie ma niczyjej winy, ale nadal są to błędy. Wyobraź sobie, że dawno, dawno temu w odległej galaktyce (w Visual Studio 6.0) powstał projekt zawierający klasę CSampleApp, następcę CWinApp. W klasie bazowej znajduje się funkcja wirtualna WinHelp. Następca nakłada tę funkcję i wykonuje wszystkie niezbędne działania. Proces ten pokazano na poniższym rysunku.

Projekt został później przeniesiony do Visual Studio 2005, gdzie zmienił się prototyp funkcji WinHelp, ale nikt tego nie zauważył, ponieważ w trybie 32-bitowym typy DWORD i DWORD_PTR były zgodne i program nadal działał poprawnie.

Błąd czeka na ujawnienie w systemie 64-bitowym, w którym typy DWORD i DWORD_PTR mają różne rozmiary. Okazuje się więc, że w trybie 64-bitowym klasy zawierają dwie RÓŻNE funkcje WinHelp, co z pewnością jest błędne. Należy pamiętać, że te pułapki można ukryć nie tylko w MFC, gdzie niektóre funkcje mają teraz argumenty innego typu, ale także w kodzie aplikacji i bibliotekach innych firm.
6.7. Diagnoza błędów ukrytych
Istnieje wiele przykładów tego typu błędów 64-bitowych. Jak widać etap poszukiwania ukrytych błędów nie jest zadaniem trywialnym, a ponadto wiele z nich będzie występowało nieregularnie i tylko przy dużych nakładach danych. Statyczne analizatory kodu dobrze radzą sobie z diagnozowaniem tego typu błędów, gdyż potrafią sprawdzić cały kod aplikacji niezależnie od danych wejściowych i częstotliwości wykonywania jej sekcji w warunkach rzeczywistych.
Analizę statyczną warto stosować zarówno na etapie przenoszenia aplikacji na platformy 64-bitowe, aby już na początku wykryć większość błędów, jak i przy dalszym rozwoju rozwiązań 64-bitowych. Analiza statyczna ostrzeże programistę i nauczy go lepiej rozumieć specyfikę błędów związanych z architekturą 64-bitową oraz pisać bardziej wydajny kod.
Gwoli sprawiedliwości należy powiedzieć, że analizatory kodu testowego Gimpel PC-Lint i Parasoft C++ posiadają zestawy reguł diagnozowania błędów 64-bitowych. Ale przede wszystkim są to analizatory ogólnego przeznaczenia i zasady diagnozowania błędów 64-bitowych są niekompletne. Po drugie, są przeznaczony głównie dla model danych LP64 używany w rodzinie OS Linux, więc nie są one tak użyteczne dla programów Windows, które wykorzystują model danych LLP64.
Krok 7 – Aktualizacja procesu testowego
Krok z poszukaj błędów w kodzie programu opisane w poprzedniej sekcji jest konieczne, ale niewystarczające. Żadna z metod, łącznie ze statyczną analizą kodu, nie gwarantuje wykrycia wszystkich błędów, a najlepszy wynik można uzyskać jedynie poprzez połączenie różnych metod.
Jeśli Twój program 64-bitowy przetwarza większy rozmiar danych niż wersja 32-bitowa, powinieneś rozszerzyć testy o przetwarzanie danych o rozmiarze większym niż 4 GB. Jest to granica, powyżej której zaczyna pojawiać się wiele błędów 64-bitowych. Testy te mogą potrwać znacznie dłużej i należy być na to przygotowanym.
Testy są zazwyczaj pisane w taki sposób, aby każdy z testów mógł przetworzyć niewielką liczbę elementów i tym samym umożliwić wykonanie wszystkich wewnętrznych testów jednostkowych w ciągu kilku minut, natomiast testy automatyczne (np. przy użyciu AutomatedQA TestComplete) można wykonać w kilka godzin.
Funkcja sortowania, która sortuje 100 elementów, prawie na pewno będzie działać poprawnie w przypadku 100000 32 elementów w systemie 64-bitowym. Jednak ta sama funkcja może zawieść w systemie 5-bitowym podczas próby przetworzenia XNUMX miliardów elementów. Szybkość wykonywania testu jednostkowego można zmniejszyć miliony razy.
Nie zapomnij o kosztach adaptacji testów podczas masteringu systemów 64-bitowych. Dobrym rozwiązaniem jest podzielenie testów jednostkowych na szybkie (pracujące z małymi rozmiarami pamięci) i wolne, które przetwarzają gigabajty i działają np. przez noc. Zautomatyzowane testowanie programów 64-bitowych wymagających dużych zasobów można zorganizować w oparciu o obliczenia rozproszone.
Ostrzeżenia
Siano jeszcze jedna nieprzyjemna rzecz. Używanie narzędzi takich jak BoundsChecker do sprawdzania błędów w programach 64-bitowych, które wymagają dużych zasobów i zużywają dużo pamięci, raczej nie powiedzie się. Powodem jest duże spowolnienie testowanych programów, co czyni to podejście bardzo niewygodnym.
W trybie diagnozowania wszelkich błędów związanych z pracą pamięci narzędzie Inspektor równoległy zawarte w Studio równoległe Intela, spowolni działanie aplikacji średnio 100 razy. Najprawdopodobniej będziesz musiał pozostawić algorytm do testowania na noc, aby zobaczyć wyniki dopiero następnego dnia, podczas gdy normalnie algorytm ten działa w ciągu zaledwie 10 minut.
Mimo to jesteśmy pewni, że Parallel Inspector jest jednym z najbardziej przydatnych i wygodnych narzędzi podczas pracy w trybie wyszukiwania błędów operacji pamięci. Trzeba tylko przygotować się na zmianę praktyki diagnozowania błędów i uwzględnić to przy planowaniu opanowania systemów 64-bitowych.

Spojrzeć na: 10 najlepszych narzędzi do naprawy systemu Windows 10
Finales Pensamientos
Nie zapomnij dodać testów sprawdzających zgodność formatu danych pomiędzy wersjami 32-bitową i 64-bitową. Zgodność danych jest często naruszana podczas migracji, ze względu na zapisywanie do plików typów takich jak size_t lub long (w systemach Linux). To był cały nasz samouczek na temat przesyłania programów z wersji 32-bitowej do 64-bitowej. Mamy nadzieję, że zakończenie procesu przebiegło pomyślnie.
Nazywam się Javier Chirinos i jestem pasjonatem technologii. Odkąd pamiętam lubiłem komputery i gry wideo, a to hobby zakończyło się pracą.
Od ponad 15 lat publikuję o technologiach i gadżetach w Internecie, szczególnie w mundobytes.com
Jestem również ekspertem w dziedzinie komunikacji i marketingu online oraz posiadam wiedzę z zakresu rozwoju WordPressa.