2005年04月30日
川俣晶の縁側ソフトウェア技術雑記total 66672 count

Visual C++でアラインメントを扱う3つの機能、#pragma pack, __declspec(align(#)), __alignof演算子についてのメモ

Written By: 川俣 晶連絡先

 出戻りC++プログラマなので、極初歩的な情報を確認しています。

 間違い等あればご教示下さい。

 構造体などのアラインメントは、16bit用ソースを32bitで使う場合に問題になる場合があります。おそらく、同様に32bit→64bitのケースでも問題になるケースがあると思われるので、今からメモっておく価値がある?

 サンプルソースはVisual Studio.NET 2003のWin32コンソールアプリケーション用で、プリコンパイルヘッダー関係は割愛しています。解説はWindowsの32bit環境であることを前提としています。

#pragma packの使用例 §

 ソースコードの途中でアラインメントを変更します。

 それだけでなく、pushとpopの機能を使うと、一時的に変更したものを戻すことができます。

 コマンドラインオプションでも変更できますが、それによってソースコードの途中で切り替えることはできません。

 サンプルソースを以下に示します。

 アラインメントを変更した構造体と、変更しない構造体のサイズを比較します。

#include "stdafx.h"

typedef struct s1

{

    char ch1;

    int i1;

} t1;

#pragma pack(push,1)

typedef struct s2

{

    char ch1;

    int i1;

} t2;

#pragma pack(pop)

int _tmain(int argc, _TCHAR* argv[])

{

    printf("size of t1=%d\n",sizeof(t1));

    printf("size of t2=%d\n",sizeof(t2));

    return 0;

}

 実行結果は以下のようになります。

size of t1=8

size of t2=5

 構造体t1では、アラインメントは4バイトであるため、1バイトしか占有しないch1の後には3バイトが詰められ、i1は4バイト目から始まります。つまり全体で8バイト占有します。

 しかし、構造体t2では、#pragma pack(push,1)によって、アラインメントを1バイトに変更しているので、詰め物の3バイトは存在せず、サイズは5バイトになります。

 そして、その後、#pragma pack(pop)によって、アラインメントを元の値(4バイト)に戻しています。

__declspec(align(#))の使用例 §

 __declspec(align(#))は、あるデータが配置される位置のアラインメントを指定します。たとえば、32bit境界に合わせた位置に置かねば正常に扱えないデータを記述する場合などに使用します。(x86では馴染みが薄いかもしれないが、X68000プログラミング経験者なら、良く分かる?)

 サンプルソースを以下に示します。

 アラインメントを指定した構造体を複数含む構造体と、そうではない構造体を複数含む構造体のサイズを比較します。

#include "stdafx.h"

typedef struct

{

    char ch1;

} a1;

typedef __declspec(align(4)) struct

{

    char ch1;

} a2;

typedef struct

{

    a1 dummy1;

    a1 dummy2;

} s1;

typedef struct

{

    a2 dummy1;

    a2 dummy2;

} s2;

int _tmain(int argc, _TCHAR* argv[])

{

    printf("size of s1=%d\n",sizeof(s1));

    printf("size of s2=%d\n",sizeof(s2));

    return 0;

}

 実行結果は以下のようになります。

size of s1=2

size of s2=8

 構造体s1とs2の定義は、#pragma packのような特殊な記述も含まず、どちらも同じように見えます。しかし、内部で使用された型(a1とa2)の定義の相違から、全体としてのサイズは一致しなくなります。つまり、charのアラインメントは1であるため、char2個を含む構造体のサイズは2バイトになりますが、__declspec(align(4))によって強制的にアラインメントを4バイトに設定された構造体は、4バイト境界に配置されるために、8バイトのサイズを占有します。

__alignof演算子の使用例 §

 指定した型のアラインメントのバイト数を取得する演算子です。

 テスト実行環境では、charなら1、intは4になります。

 サンプルソースを以下に示します。

 同じ型の値を2つ並べ、そのアドレスの差を取ってアラインメントの値を調べ、それが__alignof演算子の値と一致することを確認しています。

 (なお、このような一致は、サイズがアラインメントを上回る型では成立しないことに注意が必要)

#include "stdafx.h"

#include "assert.h"

typedef struct

{

    char c1;

    char c2;

} s1;

typedef struct

{

    short int c1;

    short int c2;

} s2;

int _tmain(int argc, _TCHAR* argv[])

{

    s1 t1;

    assert( __alignof(char) == (char*)&t1.c2 - (char*)&t1.c1 );

    s2 t2;

    assert( __alignof(short int) == (char*)&t2.c2 - (char*)&t2.c1 );

    return 0;

}

 このプログラムは、実行に成功した場合、何も表示しません。

 (char*)&t1.c2 - (char*)&t1.c1は、連続して記述されたchar型の変数のアドレスの差分を計算しています。この値は、char型のアラインメントつまり__alignof(char)の値に一致しているはずです。

 同様に、(char*)&t2.c2 - (char*)&t2.c1は、short int型で同じような計算を行っています。アドレスの差分を得るために、(char*)というキャストを付けていることに注意して下さい。これを付けないと、意図した値が取得できません。

この知識が必要とされる状況 §

 このような知識は、たとえば以下のように記述された16bit環境用のコードを32bit環境に移行させる際に必須となります。

typedef struct {

    BITMAPFILEHEADER bmfh;

    BITMAPINFOHEADER bmih;

    RGBQUAD aColors[1];

} DIB_FILE_IMAGE, FAR * LP_DIB_FILE_IMAGE;

 この構造体は、BITMAPFILEHEADERが4バイト境界で終わらないために、2バイトの詰め物が詰められ、その結果としてbmihは正しいオフセット値を持たなくなります。

 これを解決するには、手前に#pragma pack(push,2)を、後に#pragma pack(pop)を付ければOKです。

まとまらないまとめ §

 以上、完全にまとめ切れていませんが、最低限のことだけまとめておきました。

 Windows上のC++の世界は、今、32bitと64bitの狭間で流動しており、非常に興味深い状況です。これだけ強力なアラインメント制御機能が揃っていることは、64bit化で役立つツールとして準備された側面がおそらくあるのでしょう。

Facebook

キーワード【 川俣晶の縁側ソフトウェア技術雑記
【技術雑記】の次のコンテンツ
2005年
05月
04日
【削除済み】本当は凄いC++!? プログラム比較論 C++ vs C#, Java, Visual Basic
3days 0 count
total 31410 count
【技術雑記】の前のコンテンツ
2005年
04月
29日
大好きなawkをXSLTと一緒にしないで! awkとXSLTは似ている、という意見への反論!?
3days 0 count
total 3464 count

このコンテンツを書いた川俣 晶へメッセージを送る

[メッセージ送信フォームを利用する]

メッセージ送信フォームを利用することで、川俣 晶に対してメッセージを送ることができます。

この機能は、100%確実に川俣 晶へメッセージを伝達するものではなく、また、確実に川俣 晶よりの返事を得られるものではないことにご注意ください。

このコンテンツへトラックバックするためのURL

https://mag.autumn.org/tb.aspx/20050430014843
サイトの表紙【技術雑記】の表紙【技術雑記】のコンテンツ全リスト 【技術雑記】の入手全リスト 【技術雑記】のRSS1.0形式の情報このサイトの全キーワードリスト 印刷用ページ

管理者: 川俣 晶連絡先

Powered by MagSite2 Version 0.36 (Alpha-Test) Copyright (c) 2004-2021 Pie Dey.Co.,Ltd.