Author: Shinji Chikugi
Table of Contents
はじめに
その注意点について、このブログ記事でフォローアップさせていただきます。
なお、T5のセッションのビデオ視聴や資料のダウンロードは下記のURLより行なえます。(公開され次第追記します)
アライメントの違いに注意する
32-bit向けのアプリを64-bitコンパイラで単にビルドするとアライメントの不整合によってデータが正しく扱えない場合があります。この代表的な例はC言語の構造体をデータの入出力に用いている場合です。
C++言語はC言語がベースですが、C言語は「高級アセンブラ」と称されるくらいにCPUのアーキテクチャに依存したコードを記述できます。この特徴はC++言語にも引き継がれています。特にポインタやビット演算はその最たる部分です。またC言語はC++言語と互換性が高いので、C言語で書かれたコードをC++言語のプロジェクトで再利用するのは良くある手法です。(過去に自分も何度も経験があります)
またデータをファイルへ入出力するときに構造体で持つデータのイメージをそのままファイルに入出力するという実装も良く用いられます。
例えば、Windowsのビットマップファイル(*.bmp)が有名な例です。
では、同じ構造体を32-bitと64-bitで扱う場合にアライメントがどのように変わるかを実験をしてみましょう。たとえばこのような構造体があります。
1 2 3 4 5 6 7 8 |
struct MyRecord { char ch; long ID; char name[10]; long double x; long double y; float h; }; |
この構造体の情報をC++Builderのアプリケーションフォームに表示させることにします。TButtonとTMemoを貼り付けて、TButtonのクリックでTMemoに構造体のアライメントを出力するようにしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void __fastcall TForm1::Button1Click(TObject *Sender) { MyRecord rec; Memo1->Clear(); Memo1->Lines->Add(L"Size = " + UnicodeString(sizeof(rec))); Memo1->Lines->Add(L"long Size = " + UnicodeString(sizeof(rec.ID))); Memo1->Lines->Add(L"long double Size = " + UnicodeString(sizeof(rec.x))); Memo1->Lines->Add(L"Offset name = " + UnicodeString(offsetof(MyRecord, name))); Memo1->Lines->Add(L"Offset x = " + UnicodeString(offsetof(MyRecord, x))); Memo1->Lines->Add(L"Offset y = " + UnicodeString(offsetof(MyRecord, y))); Memo1->Lines->Add(L"Offset h = " + UnicodeString(offsetof(MyRecord, h))); } |
では、ビルドターゲットをWindows 32ビットとWindows 64ビットの2種類で切り替えてビルドし、TMemoに出力される結果を確認しましょう。
32-bitの場合は次のように出力されます。
1 |
Size = 56<br>long Size = 4<br>long double Size = 10<br>Offset name = 8<br>Offset x = 24<br>Offset y = 40<br>Offset h = 52 |
これに対して64-bitの場合は次のように出力されます。
1 |
Size = 48<br>long Size = 4<br>long double Size = 8<br>Offset name = 8<br>Offset x = 24<br>Offset y = 32<br>Offset h = 40 |
同じソースコードでも、ビルドターゲットの違いにより結果が一致しないことが確認できました。
アライメントが異なりますし、構造体自体のサイズも異なっています。long doubleに着目してみましょう。32-bitでは10バイトであるのに対し、64-bitでは8バイトと小さくなっていることが分かります。
もし、この構造体をそのままファイルに入出力していると、32-bit版と64-bit版のアプリでは同じデータファイルを扱うことができなくなります。
この例は非常に極端ですが、昔からの「秘伝のコード」を使っていると良くあることです。
アプリケーションの64-bit化では、単にデータ型の変換に注意するだけではなく、このような部分を精査して適切なリファクタリングを実施することが必要です。
NO_STRICTマクロの削除による厳密な型チェックの実施
古いC++Builderのバージョンでは、Windowsハンドルなどの関係で、マクロNO_STRICTを有効にしてメソッド呼び出しの厳密な型チェックを行っていない場合がありました。
10.3 Rioでは、この定義自体が削除されます。このことによりコンパイル時に厳密な型チェックが実施され、メソッドや関数呼び出し時の型安全性が担保されるようになります。
#pragma link で参照するライブラリの拡張子が異なる
DLLのインポートライブラリなどをプロジェクトファイルで指定する代わりに、ソースコードから #pragma linkで利用するテクニックがあります。
1 |
#pragma link "XercesLib.lib" |
64ビット版ではライブラリの拡張子が”.lib”ではなく、”.a”になるので、#pragma linkの見直しも必要です。
http://docwiki.embarcadero.com/RADStudio/Rio/ja/Pragma_link
おまけ?
?????
1 2 3 4 5 6 7 8 9 10 |
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include <tchar.h> //--------------------------------------------------------------------------- USEFORM("Unit1.cpp", Form1); //--------------------------------------------------------------------------- WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) { |
なぜ、_tWinMain ?
あ、_tWinMainに戻り値型の宣言がない。気をつけましょう。
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition