7 kroků k přenosu programu do 64bitového systému

Měli byste začít ovládat 64bitové systémy otázkou „Potřebujeme náš projekt přestavět na 64bitový systém? Tato otázka musí být zodpovězena, ale ne ve spěchu, po přemýšlení. Na jedné straně můžete zaostávat za svými konkurenty tím, že nenabízíte 64bitová řešení včas. Na druhou stranu můžete ztrácet čas na 64bitové aplikaci, která nebude poskytovat žádné konkurenční výhody.
Uvádíme hlavní faktory, které vám pomohou při výběru.

2.1. Délka životního cyklu aplikace

Neměli byste vytvářet 64bitovou verzi aplikace s krátkým životním cyklem. Díky subsystému WOW64 fungují staré 32bitové aplikace docela dobře na 64bitových systémech Windows a proto nemá smysl dělat 64bitový program, který za 2 roky již nebude podporován. Praxe navíc ukázala, že přechod na 64bitové verze Windows byl zpožděn a možná většina vašich uživatelů v krátké době bude používat pouze 32bitovou verzi vašeho softwarového řešení.
Pokud plánujete dlouhodobý vývoj a dlouhodobou podporu softwarového produktu, pak byste měli začít pracovat na 64bitové verzi vašeho řešení. To lze provést pomalu, ale mějte na paměti, že čím déle nemáte plnou 64bitovou verzi, tím větší potíže mohou být s podporou takové aplikace nainstalované v 64bitových verzích Windows.

2.2. Intenzita zdrojů aplikace

Překompilování programu pro 64bitový systém mu umožní využívat obrovské množství paměti RAM a také zrychlí jeho provoz o 5-15%. K 5-10% zrychlení dojde v důsledku využití architektonických možností 64bitového procesoru, např. většího počtu registrů. Další 1%-5% nárůst rychlosti je způsoben absencí vrstvy WOW64, která překládá volání API mezi 32bitovými aplikacemi a 64bitovým operačním systémem.
Pokud váš program nepracuje s velkým objemem dat (více než 2GB) a jeho rychlost není kritická, není přechod na 64bitový systém v blízké budoucnosti tak relevantní.
Mimochodem, i jednoduché 32bitové aplikace mohou těžit z jejich provozu v 64bitovém prostředí. Pravděpodobně víte, že program zkompilovaný s klíčem /LARGEADDRESSAWARE:YES může alokovat až 3 gigabajty paměti, pokud je 32bitový operační systém Windows spuštěn pomocí klíče /3gb. Stejný 32bitový program běžící na 64bitovém systému dokáže alokovat téměř 4 GB paměti (v praxi asi 3,5 GB).

2.3. Rozvoj knihovny

Pokud vyvíjíte knihovny, komponenty nebo jiné prvky, které vývojáři třetích stran používají k vytváření svého softwaru, musíte být proaktivní při vytváření 64bitové verze vašeho produktu. V opačném případě budou vaši zákazníci se zájmem o vydání 64bitových verzí nuceni hledat alternativní řešení. Někteří vývojáři softwarové a hardwarové ochrany například zareagovali s velkým zpožděním na vznik 64bitových programů, což donutilo řadu klientů hledat jiné nástroje k ochraně svých programů.
Další výhodou vydání 64bitové verze knihovny je, že ji můžete prodávat jako samostatný produkt. Vaši zákazníci, kteří chtějí vytvářet 32bitové i 64bitové aplikace, tak budou nuceni zakoupit 2 různé licence. Tuto zásadu používá například společnost Spatial Corporation při prodeji knihovny Spatial ACIS.

2.4. Závislost vašeho produktu na knihovnách třetích stran

Než plánujete vytvořit 64bitové verze svého produktu, zjistěte, zda existují 64bitové verze knihoven a komponent, které používá. Zjistěte také, jaká je cenová politika pro 64bitovou verzi knihovny. To vše lze zjistit na webových stránkách vývojáře knihovny. Pokud podpora není k dispozici, hledejte předem alternativní řešení, která podporují 64bitové systémy.

2.5. Dostupnost 16bitových aplikací

Pokud vaše řešení stále obsahují 16bitové moduly, pak je čas se jich zbavit. 16bitové aplikace nejsou podporovány v 64bitových verzích systému Windows.
Zde bychom měli objasnit jeden bod související s používáním 16bitových instalačních programů. Stále se používají k instalaci některých 32bitových aplikací. Byl vytvořen speciální mechanismus, který za běhu nahrazuje řadu nejoblíbenějších 16bitových instalátorů novějšími verzemi. To může způsobit mylnou představu, že 16bitové programy stále běží v 64bitovém prostředí. Pamatujte, že to není pravda.

2.6. Dostupnost kódu assembleru

Nezapomeňte, že použití velkého množství kódu sestavení může výrazně zvýšit náklady na vytvoření 64bitové verze aplikace.
Po zvážení všech uvedených faktů, všech pro a proti se rozhodněte, zda byste měli svůj projekt portovat na 64bitové systémy. A pokud je to tak, tak pojďme dál.

3. Krok tři. Nástroje

To, že jste se rozhodli vyvinout 64bitovou verzi svého produktu a jste ochotni tomu věnovat čas, nezaručuje úspěch. Faktem je, že musíte mít všechny potřebné nástroje a zde může dojít k nepříjemným incidentům.
Nejjednodušším, ale také nejnepřekonatelnějším problémem může být absence 64bitového kompilátoru. Článek je napsán v roce 2009, ale stále neexistuje žádný 64bitový kompilátor C++ Builder od Codegear. Jeho vydání se očekává až do konce letošního roku. Obejít podobný problém je nemožné, pokud samozřejmě nepřepíšete celý projekt například pomocí Visual Studia. Pokud je však vše jasné s absencí 64bitového kompilátoru, mohou se další podobné problémy ukázat jako skrytější a objevit se již ve fázi převodu projektu na novou architekturu. Proto bych vám rád doporučil, abyste si předem prozkoumali, zda existují všechny potřebné komponenty, které budou nutné k implementaci 64bitové verze vašeho produktu. Mohou vás čekat nepříjemná překvapení.
Není samozřejmě možné zde vyjmenovat vše, co by mohlo být pro projekt potřeba, ale přesto vám nabídnu seznam, který vám pomůže se zorientovat a možná si vzpomenete na další body, které jsou nutné k realizaci vašeho 64bitového projektu:

3.1. Dostupnost 64bitového kompilátoru

Je těžké říci něco více o důležitosti 64bitového kompilátoru. Prostě to musí být.
Pokud plánujete vyvíjet 64bitové aplikace pomocí nejnovější verze (v době psaní tohoto článku) sady Visual Studio 2008, následující tabulka N2 vám pomůže určit, kterou edici sady Visual Studio potřebujete.


Tabulka N2. Funkce různých edic Visual Studio 2008

3.2. Dostupnost 64bitových počítačů s 64bitovými operačními systémy

Virtuální stroje můžete samozřejmě používat ke spouštění 64bitových aplikací na 32bitovém hardwaru, ale to je extrémně nepohodlné a nezajistí požadovanou úroveň testování. Je vhodné, aby stroje měly nainstalované alespoň 4-8 gigabajtů RAM.

3.3. Dostupnost 64bitových verzí všech použitých knihoven

Pokud jsou knihovny poskytovány ve zdrojovém kódu, musí být přítomna 64bitová konfigurace projektu. Upgradovat knihovnu sami, abyste ji sestavili pro 64bitový systém, může být nevděčný a obtížný úkol a výsledek může být nespolehlivý a náchylný k chybám. Můžete tím také porušit licenční smlouvy. Pokud používáte knihovny jako binární moduly, měli byste také zjistit, zda existují 64bitové moduly. V 64bitové aplikaci nebudete moci používat 32bitové knihovny DLL. Můžete vytvořit speciální svazek přes COM, ale to bude samostatný velký a komplexní úkol. Upozorňujeme také, že nákup 64bitové verze knihovny může stát další peníze.

3.4. Nedostatek vestavěného kódu assembleru

Visual C++ nepodporuje 64bitový inline assembler. Musíte použít buď externí 64bitový assembler (například MASM) nebo mít implementaci C/C++ stejné funkce.

3.5. Modernizace metodiky testování

Významná revize metodiky testování, modernizace unit testů, využití nových nástrojů. To bude podrobněji probráno níže, ale nezapomeňte to vzít v úvahu při odhadu času stráveného migrací aplikace do nového systému.

3.6. Nová data pro testování

Pokud vyvíjíte aplikace náročné na zdroje, které spotřebovávají velké množství paměti RAM, pak se musíte postarat o doplnění databáze testovacích vstupních dat. Při zátěžovém testování 64bitových aplikací je vhodné jít za hranici 4 gigabajtů spotřeby paměti. Mnoho chyb se může objevit pouze za takových podmínek.

3.7. Dostupnost 64bitových ochranných systémů

Použitý ochranný systém musí podporovat 64bitové systémy v plném rozsahu, který požadujete. Například Aladdin rychle vydal 64bitové ovladače pro podporu hardwarových klíčů Hasp. Ale velmi dlouhou dobu neexistoval systém pro automatickou ochranu 64bitových binárních souborů (program Hasp Envelop). Ochranný mechanismus tedy musel být implementován nezávisle v rámci programového kódu, což byl další složitý úkol, který vyžadoval kvalifikaci a čas. Nezapomeňte na podobné problémy týkající se bezpečnosti, systému aktualizací a tak dále.

3.8. Instalátor

Potřebujete nový instalační program, který dokáže plně nainstalovat 64bitové aplikace. Hned bych vás chtěl upozornit na jednu tradiční chybu. Toto je vytvoření 64bitových instalačních programů pro instalaci 32/64bitových softwarových produktů. Při přípravě 64bitové verze aplikace ji vývojáři často chtějí udělat kompletně 64bitovou. A vytvoří 64bitový instalační program, přičemž zapomenou, že uživatelé 32bitového operačního systému takový instalační balíček jednoduše nespustí. Upozorňujeme, že se nespustí 32bitová aplikace, která je součástí distribuce spolu s 64bitovou, ale samotný instalátor. Pokud je totiž distribuce 64bitová aplikace, pak samozřejmě nepoběží na 32bitovém operačním systému. Nejotravnější na tom je, že uživatel nebude schopen odhadnout, co se děje. Jednoduše uvidí instalační balíček, který nelze spustit.

4. Krok čtyři. Nastavení projektu ve Visual Studiu 2005/2008

Vytvoření konfigurace 64bitového projektu ve Visual Studiu 2005/2008 vypadá docela jednoduše. Potíže vás čekají ve fázi sestavování nové konfigurace a hledání chyb v ní. Chcete-li vytvořit 64bitovou konfiguraci, stačí provést následující 4 kroky:
Spusťte správce konfigurace, jak je znázorněno na obrázku N1:


Obrázek 1. Spuštění Configuration Manager

Ve správci konfigurace vyberte podporu pro novou platformu (obrázek N2):


Obrázek 2. Vytvoření nové konfigurace

Vybereme 64bitovou platformu (x64) a jako základ vybereme nastavení z 32bitové verze (obrázek N3). Prostředí Visual Studio upraví nastavení, která ovlivňují samotný režim sestavení.


Obrázek 3. Vyberte x64 jako platformu a jako základ vezměte konfiguraci Win32

Přidání nové konfigurace je dokončeno a můžeme vybrat možnost 64bitové konfigurace a začít kompilovat 64bitovou aplikaci. Volba 64bitové konfigurace pro sestavení je znázorněna na obrázku N4.


Obrázek 4. Nyní je k dispozici 32bitová a 64bitová konfigurace

Pokud budete mít štěstí, nebude potřeba dodatečně konfigurovat 64bitový projekt. To ale značně závisí na projektu, jeho složitosti a počtu použitých knihoven. Jediné, co je potřeba hned změnit, je velikost zásobníku. Pokud váš projekt používá výchozí velikost zásobníku, tedy 1 megabajt, pak má smysl nastavit ji pro 64bitovou verzi na 2 megabajty. Není to nutné, ale je lepší být předem na bezpečné straně. Pokud používáte velikost zásobníku odlišnou od výchozí velikosti, pak má smysl ji pro 64bitovou verzi 2krát zvětšit. Chcete-li to provést, v nastavení projektu vyhledejte a změňte parametry Stack Reserve Size a Stack Commit Size.

5. Krok 5. Kompilace aplikace

Zde by bylo dobré pohovořit o typických problémech, které se objevují ve fázi kompilace 64bitové konfigurace. Zvažte, jaké problémy vznikají s knihovnami třetích stran, řekněte, že kompilátor v kódu spojeném s funkcemi WInAPI již nebude umožňovat umístění ukazatele do typu LONG a budete muset svůj kód modernizovat a použít typ LONG_PTG. A mnoho mnoho dalších. Bohužel je toho tolik a chyby jsou tak rozmanité, že to není možné podat v jednom článku nebo snad ani v knize. Budete muset projít všechny chyby, které kompilátor vytvoří, a nová varování, která tam dříve nebyla, a v každém jednotlivém případě zjistit, jak modernizovat kód.
Sbírka odkazů na zdroje věnované vývoji 64bitových aplikací může život částečně usnadnit: http://www.viva64.com/links/64-bit-development/. Sborník je neustále aktualizován a autor bude čtenářům vděčný, pokud mu pošlou odkazy na zdroje, které si podle nich zaslouží pozornost.
My se zde zaměříme pouze na typy, které mohou být zajímavé pro vývojáře při migraci aplikací. Tyto typy jsou uvedeny v tabulce N3. Většina chyb při kompilaci bude spojena s použitím těchto typů.
Typ Typ rozměru na platformě x32 / x64 Poznámka
int 32 / 32 Základní typ. Na 64bitových systémech zůstává 32bitový.
dlouho 32 / 32 Základní typ. Na 64bitových systémech Windows zůstává 32bitový. Upozorňujeme, že na 64bitových systémech Linux byl tento typ rozšířen na 64bitové. Nezapomeňte na to, pokud vyvíjíte kód, který by měl fungovat a kompilovat pro systémy Windows a Linux.
velikost_t 32 / 64 Základní nepodepsaný typ. Velikost typu je zvolena tak, aby pojala maximální velikost teoreticky možného pole. Ukazatel lze bezpečně umístit do typu size_t (výjimkou jsou ukazatele na funkce třídy, ale to je speciální případ).
ptrdiff_t 32 / 64 Podobné jako typ size_t, ale podepsané. Výsledek výrazu, kde je jeden ukazatel odečten od druhého (ptr1-ptr2), bude typu ptrdiff_t.
Ukazatel 32 / 64 Velikost ukazatele přímo závisí na bitové kapacitě platformy. Buďte opatrní při odesílání ukazatelů na jiné typy.
__int64 64 / 64 Podepsaný 64bitový typ.
DWORD 32 / 32 32bitový typ bez znaménka. Deklarováno ve WinDef.h jako: typedef unsigned long DWORD;
DWORDLONG 64 / 64 64bitový typ bez znaménka. Deklarováno ve WinNT.h jako: typedef ULONGLONG DWORDLONG;
DWORD_PTR 32 / 64 Nepodepsaný typ, který může obsahovat ukazatel. Deklarováno v BaseTsd.h jako: typedef ULONG_PTR DWORD_PTR;
DWORD32 32 / 32 32bitový typ bez znaménka. Deklarováno v BaseTsd.h jako: typedef unsigned int DWORD32;
DWORD64 64 / 64 64bitový typ bez znaménka. Deklarováno v BaseTsd.h jako: typedef unsigned __int64 DWORD64;
HALF_PTR 16 / 32 Půlka ukazatele. Deklarováno v Basetd.h jako:#ifdef _WIN64 typedef int HALF_PTR;#else typedef short HALF_PTR;#endif
INT_PTR 32 / 64 Podepsaný typ, do kterého lze umístit ukazatel. Deklarováno v BaseTsd.h jako:#if define(_WIN64) typedef __int64 INT_PTR; #else typedef int INT_PTR;#endif
DLOUHO 32 / 32 Podepsaný typ, který zůstává 32bitový. Proto by se nyní v mnoha případech mělo používat LONG_PTR. Deklarováno ve WinNT.h jako: typedef long LONG;
LONG_PTR 32 / 64 Podepsaný typ, do kterého lze umístit ukazatel. Deklarováno v BaseTsd.h jako:#if define(_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR;#endif
LPARAM 32 / 64 Parametr pro odesílání zpráv. Deklarováno ve WinNT.h jako: typedef LONG_PTR LPARAM;
SIZE_T 32 / 64 Analogicky k typu size_t. Deklarováno v BaseTsd.h jako: typedef ULONG_PTR SIZE_T;
SSIZE_T 32 / 64 Analogicky k typu ptrdiff_t. Deklarováno v BaseTsd.h jako: typedef LONG_PTR SSIZE_T;
ULONG_PTR 32 / 64 Nepodepsaný typ, který může obsahovat ukazatel. Deklarováno v BaseTsd.h jako:#if define(_WIN64) typedef unsigned __int64 ULONG_PTR;#else typedef unsigned long ULONG_PTR;#endif
SLOVO 16 / 16 16bitový typ bez znaménka. Deklarováno ve WinDef.h jako: typedef unsigned short WORD;
WPARAM 32 / 64 Parametr pro odesílání zpráv. Deklarováno ve WinDef.h jako: typedef UINT_PTR WPARAM;

Tabulka N3. Typy zájmu při portování 32bitových programů na 64bitové systémy Windows.

6. Diagnostika skrytých chyb

Pokud si myslíte, že po opravě všech chyb kompilace bude získána dlouho očekávaná 64bitová aplikace, pak budete muset být zklamáni. Nejtěžší část teprve přijde. Ve fázi kompilace opravíte nejzjevnější chyby, které mohl kompilátor odhalit a které souvisejí především s nemožností implicitního přetypování. Ale tohle je špička ledovce. Většina chyb je skryta. Z hlediska abstraktního jazyka C++ vypadají tyto chyby bezpečně nebo jsou maskovány explicitním přetypováním. Takových chyb je několikanásobně více, než je počet chyb zjištěných ve fázi kompilace.
Neměli byste vkládat své naděje do klíče /Wp64. Tento klíč je často nabízen jako zázračný nástroj pro hledání 64bitových chyb. Přepínač /Wp64 ve skutečnosti umožňuje pouze při kompilaci 32bitového kódu přijímat varování, že určité části kódu budou v 64bitovém režimu nesprávné. Při kompilaci 64bitového kódu budou tato varování kompilátorem v každém případě vydána. A proto je při kompilaci 64bitové aplikace klíč /Wp64 ignorován. A ještě více tento klíč nepomůže při hledání skrytých chyb.
Podívejme se na několik příkladů skrytých chyb.

6.1. Explicitní typové obsazení

Nejjednodušší, ale ne nejsnáze zjistitelná třída chyb je spojena s přetypováním explicitního typu, při kterém jsou významné bity oříznuty.
Běžným příkladem je přetypování ukazatelů na 32bitové typy při jejich předávání funkcím, jako je SendMessage:
MyObj* pObj = ... ::SendMessage(hwnd, msg, (WORD)x, (DWORD)pObj);

Zde se k převodu ukazatele na číselný typ používá explicitní přetypování. Pro 32bitovou architekturu je výše uvedený příklad správný, protože poslední parametr funkce SendMessage je typu LPARAM, což je stejné jako DWORD na 32bitové architektuře. Pro 64bitovou architekturu je použití DWORD chybné a mělo by být nahrazeno LPARAM. Typ LPARAM má velikost 32 nebo 64 bitů v závislosti na architektuře.
Toto je jednoduchý případ, ale často typové obsazení vypadá sofistikovaněji a nelze jej zjistit pomocí varování kompilátoru nebo prohledáváním textu programu. Explicitní přetypování typu potlačuje diagnostiku kompilátoru, protože jejich účelem je sdělit kompilátoru, že přetypování typu je správné a programátor přijal odpovědnost za bezpečnost kódu. Nepomůže ani explicitní hledání. Typy nemusí mít standardní názvy (určené programátorem prostřednictvím typedef) a existuje také několik způsobů, jak implementovat explicitní přetypování. Pro spolehlivou diagnostiku takových chyb je třeba používat pouze speciální nástroje, jako jsou analyzátory Viva64 nebo PC-Lint.

6.2. Implicitní typové obsazení

Další příklad je spojen s přetypováním implicitního typu, které také vede ke ztrátě významných bitů. Kód funkce fread čte ze souboru, ale při pokusu o načtení více než 2 gigabajtů dat na 64bitovém systému je nesprávný.
size_t __fread(void * __restrict buf, size_t size, size_t count, FILE * __restrict fp); size_t fread(void * __restrict buf, size_t size, size_t count, FILE * __restrict fp) ( int ret; FLOCKFILE(fp); ret = __fread(buf, velikost, počet, fp); FUNLOCKFILE(fp); return (ret) ;)

Funkce __fread vrací typ size_t, ale typ int se používá k uložení počtu přečtených bajtů. Výsledkem je, že při velkém množství čtených dat může funkce vrátit jiný počet bajtů, než bude skutečně načten.
Můžete říci, že se jedná o ignorantský kód pro začátečníky, že překladač ohlásí přetypování takového typu a že obecně lze takový kód snadno najít a opravit. To je teoretické. Ale v reálném životě u velkých projektů může být všechno jinak. Tento příklad je převzat ze zdrojového kódu FreeBSD. Chyba byla opravena až v prosinci 2008! A to navzdory skutečnosti, že první (experimentální) 64bitová verze FreeBSD byla vydána již v červnu 2003.
Zde je zdrojový kód před opravou:
http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdio/fread.c?rev=1.14
A zde je opravená verze (prosinec 2008):
http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdio/fread.c?rev=1.15

6.3. Práce s bity, směny

Je snadné dělat chyby v kódu, který pracuje s jednotlivými bity. Další typ chyby je spojen s operacemi směny. Podívejme se na příklad:
ptrdiff_t SetBitN(hodnota ptrdiff_t, bitNum bez znaménka) ( maska ​​ptrdiff_t = 1<< bitNum; return value | mask; }

Výše uvedený kód funguje na 32bitové architektuře a umožňuje nastavit bity číslované od 0 do 31 ku jedné. Po převedení programu na 64bitovou platformu bude potřeba nastavit bity od 0 do 63. Tento kód ale nikdy nenastaví bity očíslované 32-63. Vezměte prosím na vědomí, že „1“ je typu int a při posunutí o 32 pozic dojde k přetečení, jak je znázorněno na obrázku 5. Zda je výsledek 0 (obrázek 5-B) nebo 1 (obrázek 5-C), závisí na kompilátoru implementace.


Obrázek 5. A - Správná instalace 31. bitu v 32bitovém kódu; B,C – Chyba při instalaci 32. bitu na 64bitový systém (dvě možnosti chování)

Chcete-li kód opravit, musíte vytvořit konstantu „1“ stejného typu jako proměnná masky:

ptrdiff_t mask = ptrdiff_t(1)<< bitNum;

Všimněte si také, že neopravený kód povede k další zajímavé chybě. Při nastavení 31 bitů na 64bitovém systému bude výsledkem funkce hodnota 0xffffffff80000000 (viz obrázek 6). Výsledek výrazu 1<< 31 является отрицательное число -2147483648. Это число представляется в 64-битной целой переменной как 0xffffffff80000000.


Obrázek 6. Chyba při instalaci 31. bitu na 64bitový systém

6.4. Magická čísla

Magické konstanty, tedy čísla sloužící k nastavení velikosti konkrétního typu, mohou způsobit spoustu problémů. Správným řešením je pro tyto účely použít operátory sizeof(), ale ve velkém programu může dojít ke ztrátě starého kódu, kde byli pevně přesvědčeni, že velikost ukazatele je 4 bajty a typ size_t je vždy 32 bitů. . Tyto chyby obvykle vypadají takto:
size_t Velikost pole = N * 4; size_t *Array = (size_t *)malloc(ArraySize);

Hlavní čísla, která je třeba brát s rezervou při přechodu na 64bitovou platformu, jsou uvedeny v tabulce N4.


Tabulka N4. Základní magické hodnoty, které jsou nebezpečné při migraci aplikací z 32bitové na 64bitovou platformu

6.5. Chyby při použití 32bitových proměnných jako indexů

V programech, které zpracovávají velké množství dat, může docházet k chybám spojeným s indexováním velkých polí nebo může docházet k věčným smyčkám. Následující příklad obsahuje 2 chyby najednou:
const size_t size = ...; char *pole = ...; char *konec = pole + velikost; for (unsigned i = 0; i != size; ++i) ( const int one = 1; end[-i - one] = 0; )

První chybou je, že pokud velikost zpracovávaných dat přesáhne 4 gigabajty (0xFFFFFFFF), může dojít k věčné smyčce, protože proměnná "i" je typu "unsigned" a nikdy nedosáhne hodnoty 0xFFFFFFFF. Konkrétně píšu, že výskyt je možný, ale nemusí k němu nutně dojít. Záleží na tom, jaký kód kompilátor sestaví. Například v režimu ladění bude přítomna věčná smyčka, ale v kódu uvolnění smyčka zmizí, takže se kompilátor rozhodne optimalizovat kód pomocí 64bitového registru pro čítač a smyčka bude správná. To vše přispívá ke zmatku a kód, který fungoval včera, může druhý den náhle přestat fungovat.
Druhá chyba je spojena s průchodem polem od konce k začátku, pro které se používají záporné hodnoty indexu. Výše uvedený kód funguje ve 32bitovém režimu, ale když je spuštěn na 64bitovém počítači, při úplně první iteraci smyčky bude pole zpřístupněno za hranicemi a program se zhroutí. Podívejme se na důvod tohoto chování.

Podle pravidla jazyka C++ na 32bitovém systému bude výraz „-i - one“ vyhodnocen následovně (v prvním kroku i = 0):

  1. Výraz "-i" je typu bez znaménka a má hodnotu 0x00000000u.
  2. Proměnná "one" bude rozšířena z typu "int" na typ unsigned a bude se rovnat 0x00000001u. Poznámka: Typ int je rozšířen (podle standardu jazyka C++) na typ "unsigned", pokud se účastní operace, kde je druhý argument typu unsigned.
  3. Dojde k operaci odečítání, která zahrnuje dvě hodnoty typu unsigned a výsledek operace je 0x00000000u - 0x00000001u = 0xFFFFFFFFu. Všimněte si, že výsledek je typu bez znaménka.
  4. Na 32bitovém systému je přístup k poli na indexu 0xFFFFFFFFu ekvivalentní použití indexu -1. To znamená, že end je analogický s end[-1]. V důsledku toho je prvek pole zpracován správně.
V 64bitovém systému v posledním odstavci bude obrázek jiný. Typ bez znaménka bude rozšířen na podepsaný ptrdiff_t a index pole se bude rovnat 0x00000000FFFFFFFFi64. V důsledku toho bude pole mimo rozsah.
Chcete-li opravit kód, musíte použít typy jako ptrdiff_t a size_t.

6.6. Chyby spojené se změnou typů používaných funkcí

Jsou chyby, za které obecně nikdo nemůže, ale to jim nebrání v tom, aby byly chybami. Představte si, že kdysi dávno ve vzdálené galaxii (ve Visual Studiu 6.0) byl vyvinut projekt, který obsahoval třídu CSampleApp, která je potomkem CWinApp. Základní třída má virtuální funkci s názvem WinHelp. Dědic tuto funkci potlačí a provede potřebné úkony. To je vizuálně znázorněno na obrázku 7.


Obrázek 7. Správný funkční kód vytvořený v sadě Visual Studio 6.0

Poté se projekt přenese do Visual Studia 2005, kde se prototyp funkce WinHelp změnil, ale nikdo si toho nevšimne, protože v 32bitovém režimu jsou typy DWORD a DWORD_PTR stejné a program nadále funguje správně (obrázek 8 ).


Obrázek 8. Nesprávný, ale funkční 32bitový kód

Chyba čeká, až se projeví na 64bitovém systému, kde je velikost typů DWORD a DWORD_PTR odlišná (obrázek 9). Ukazuje se, že v 64bitovém režimu třídy obsahují dvě ODLIŠNÉ funkce WinHelp, což je přirozeně nesprávné. Upozorňujeme, že takové pasti mohou být skryty nejen v MFC, kde některé funkce změnily typy svých argumentů, ale také v kódu vašich aplikací a knihoven třetích stran.


Obrázek 9. Chyba se projevuje v 64bitovém kódu

6.7. Diagnostika skrytých chyb

Mohou být uvedeny a uvedeny příklady takových 64bitových chyb. Koho takové chyby zajímají a chtějí se o nich dozvědět více, bude zajímat článek “20 ​​úskalí portování kódu C++ na 64bitovou platformu.”
Jak vidíte, fáze hledání skrytých chyb je netriviální úkol, zejména proto, že se mnoho z nich bude objevovat nepravidelně nebo pouze na velkých vstupních objemech dat. Statické analyzátory kódu jsou vhodné pro diagnostiku takových chyb, protože dokážou zkontrolovat celý aplikační kód bez ohledu na vstupní data a frekvenci provádění jeho částí v reálných podmínkách. Má smysl používat statickou analýzu jak ve fázi portování aplikace na 64bitové platformy za účelem nalezení většiny chyb v úplné počáteční fázi, tak při dalším vývoji 64bitových řešení. Statická analýza upozorní a naučí programátora lépe porozumět vlastnostem chyb spojených s 64bitovou architekturou a psát efektivnější kód. Autorem článku je vývojář jednoho z těchto specializovaných analyzátorů kódu s názvem Viva64. S nástrojem se můžete blíže seznámit a stáhnout si demoverzi ze stránek společnosti Software Verification Systems LLC.
Abychom byli spravedliví, je třeba říci, že analyzátory kódu jako Gimpel PC-Lint a Parasoft C++Test mají sadu pravidel pro diagnostiku 64bitových chyb. Ale za prvé jsou to analyzátory pro všeobecné použití a pravidla pro diagnostiku 64bitových chyb jsou v nich špatně zastoupena. Za druhé, jsou více zaměřeny na datový model LP64 používaný v rodině operačního systému Linux, což snižuje jejich užitečnost pro programy Windows, které používají datový model LLP64.

7. Krok sedm. Modernizace testovacího procesu

Krok nalezení chyb v kódu programu popsaný v předchozí části je nezbytným, ale ne dostatečným krokem. Žádná metoda, včetně statické analýzy kódu, neposkytuje úplnou záruku detekce všech chyb a nejlepšího výsledku lze dosáhnout pouze kombinací různých technik.
Pokud váš 64bitový program zpracovává více dat než 32bitová verze, musíte testy rozšířit o zpracování dat větších než 4 gigabajty. To je hranice, za kterou se začíná projevovat mnoho 64bitových chyb. Takové testy mohou trvat řádově déle a je třeba se na to předem připravit. Obvykle jsou testy psány tak, aby zpracovaly malý počet prvků v každém testu, a tím byly schopny projít například všemi interními jednotkovými testy? za několik minut a automatizované testy (například pomocí AutomatedQA TestComplete) za několik hodin. Třídicí funkce na 32bitovém systému, pokud seřadí 100 prvků, se téměř zaručeně bude chovat správně na 100 000 prvcích. Ale stejná funkce na 64bitovém systému může selhat při pokusu o zpracování 5 miliard prvků. Rychlost provádění testu jednotky se může snížit milionkrát. Při ovládání 64bitových systémů nezapomeňte započítat náklady na přizpůsobení testů. Jedním z řešení je rozdělení unit testů na rychlé (pracující s malým množstvím paměti) a pomalé, zpracovávající gigabajty a běžící například v noci. Automatizované testování 64bitových programů náročných na zdroje lze sestavit na základě distribuovaného počítání.

Štítky:

  • 64bitové programování
  • 64bitový
  • Viva64
  • Intel 64
  • AMD64
  • c++
  • 64bitový
Přidat štítky