web-dev-qa-db-ja.com

#プラグマパック効果

#pragma packプリプロセッサステートメントが何をするのか、さらに重要なことに、なぜそれを使用したいのかを誰かが説明してくれないかと思っていました。

MSDNページ をチェックアウトしましたが、これはある程度の洞察を提供しましたが、経験のある人からもっと聞いてみたいと思っていました。以前はコードで見たことがありますが、もうどこにあるかわかりません。

199
Cenoc

#pragma packは、特定のアライメントで構造体メンバーをパックするようコンパイラーに指示します。ほとんどのコンパイラは、構造体を宣言するときに、メンバーの間にパディングを挿入して、メモリ内の適切なアドレス(通常は型のサイズの倍数)に整列するようにします。これにより、適切にアライメントされていない変数へのアクセスに関連する一部のアーキテクチャでのパフォーマンスの低下(または完全なエラー)が回避されます。たとえば、4バイトの整数と次の構造体がある場合:

struct Test
{
   char AA;
   int BB;
   char CC;
};

コンパイラは、次のようにメモリに構造体を配置することを選択できます。

|   1   |   2   |   3   |   4   |  

| AA(1) | pad.................. |
| BB(1) | BB(2) | BB(3) | BB(4) | 
| CC(1) | pad.................. |

また、sizeof(Test)は、6バイトのデータしか含まれていなくても4×3 = 12になります。 #pragmaの最も一般的な使用例は、コンパイラがデータにパディングを挿入しないようにする必要があるハードウェアデバイスを使用する場合で、各メンバーは前のものに従います。 #pragma pack(1)を使用すると、上記の構造体は次のようにレイアウトされます。

|   1   |

| AA(1) |
| BB(1) |
| BB(2) |
| BB(3) |
| BB(4) |
| CC(1) |

そしてsizeof(Test)は1×6 = 6になります。

#pragma pack(2)を使用すると、上記の構造体は次のようにレイアウトされます。

|   1   |   2   | 

| AA(1) | pad.. |
| BB(1) | BB(2) |
| BB(3) | BB(4) |
| CC(1) | pad.. |

そして、sizeof(Test)は2×4 = 8になります。

構造体の変数の順序も重要です。次のような順序の変数を使用します。

struct Test
{
   char AA;
   char CC;
   int BB;
};

#pragma pack(2)を使用すると、構造体は次のようにレイアウトされます。

|   1   |   2   | 

| AA(1) | CC(1) |
| BB(1) | BB(2) |
| BB(3) | BB(4) |

sizeOf(Test)は3×2 = 6になります。

364
Nick Meyer

#pragmaは、非ポータブル(このコンパイラーのみ)メッセージをコンパイラーに送信するために使用されます。特定の警告の無効化や構造体のパッキングなどが一般的な理由です。特定の警告を無効にすることは、エラーフラグをオンにして警告を使用してコンパイルする場合に特に役立ちます。

#pragma packは、パックされる構造体のメンバーを整列させないことを示すために特に使用されます。ハードウェアの一部にメモリマップされたインターフェイスがあり、異なる構造体メンバーが指す場所を正確に制御できるようにする必要がある場合に役立ちます。ほとんどのマシンは整列されたデータの処理がはるかに高速であるため、特に速度の最適化には適していません。

22
nmichaels

構造内のオブジェクトを整列させる境界をコンパイラーに伝えます。たとえば、次のようなものがある場合:

struct foo { 
    char a;
    int b;
};

典型的な32ビットマシンでは、通常、abの間に3バイトのパディングを入れて、bがアクセス速度を最大化するために4バイト境界に到達するようにします(これが通常デフォルトで行われます) )。

ただし、外部定義された構造と一致させる必要がある場合は、コンパイラがその外部定義に従って構造を正確にレイアウトするようにします。この場合、コンパイラに#pragma pack(1)を与えて、それを伝えることができますnotメンバー間にパディングを挿入します-構造体の定義にメンバー間のパディングが含まれている場合、明示的に挿入します(たとえば、通常unusedNまたはignoreNという名前のメンバー、またはその順序のメンバー)。

15
Jerry Coffin

データ要素(クラスや構造体のメンバーなど)は通常、アクセス時間を改善するために、現在の世代のプロセッサのWordまたはDWORDの境界に配置されます。 4で割り切れないアドレスでDWORDを取得するには、32ビットプロセッサで少なくとも1つの追加CPUサイクルが必要です。だから、例えば3つのcharメンバーchar a, b, c;、実際には6または12バイトのストレージを使用する傾向があります。

#pragmaを使用すると、これをオーバーライドして、アクセス速度を犠牲にして、または異なるコンパイラターゲット間で格納されたデータの一貫性を犠牲にして、より効率的なスペース使用を実現できます。この16ビットから32ビットコードへの移行はとても楽しかったです。 64ビットコードへの移植は、一部のコードで同じ種類の頭痛の種になると予想しています。

7
Pontus Gagge

コンパイラmay特定のアーキテクチャでのパフォーマンス上の理由から、特定のバイト境界に構造体メンバーを配置します。これにより、メンバー間に未使用のパディングが残る場合があります。構造パッキングにより、メンバーは連続します。

これは、たとえば、特定のファイルまたは通信フォーマットに準拠する構造が必要な場合に重要です。データが必要なデータは、シーケンス内の特定の位置にある必要があります。ただし、このような使用法はエンディアンネスの問題を処理しないため、使用されても、移植性がない場合があります。

また、UARTやUSBコントローラーなどのI/Oデバイスの内部レジスター構造を正確にオーバーレイして、レジスターアクセスが直接アドレスではなく構造体を介して行われるようにすることもできます。

2
Clifford

コンパイラーは、特定のプラットフォームで最大のパフォーマンスを達成するために、構造内のメンバーを調整できます。 #pragma packディレクティブを使用すると、そのアライメントを制御できます。通常、最適なパフォーマンスを得るためにデフォルトでそれを残す必要があります。リモートマシンに構造体を渡す必要がある場合は、通常、#pragma pack 1を使用して不要なアライメントを除外します。

レジスタの順序付けとアライメントに厳しい要件があるハードウェア(メモリマップデバイスなど)にコーディングしている場合にのみ、これを使用する可能性があります。

ただし、これはその目的を達成するためのかなり鈍いツールのように見えます。より良いアプローチは、このプラグマをいじるのではなく、アセンブラでミニドライバをコーディングし、C呼び出しインターフェースを与えることです。

1
msw

以前のコードで使用したことがありますが、レガシーコードとのインターフェースにのみ使用しました。これは、以前のCarbonバージョン(それ自体が元のM68k System 6.5バージョンと下位互換性があった...アイデアを得る)から設定ファイルをロードする必要があるMac OS X Cocoaアプリケーションでした。元のバージョンの設定ファイルは、構成構造のバイナリダンプであり、#pragma pack(1)を使用して余分なスペースを取り、ジャンク(つまり、構造内に存在するパディングバイト)を節約しました。

コードの元の作成者は、#pragma pack(1)を使用して、プロセス間通信でメッセージとして使用された構造を保存していました。ここでの理由は、パディングサイズが不明または変更される可能性を回避するためだと思います。コードは、先頭(ewww)からバイト数をカウントすることでメッセージ構造の特定の部分を見ることがあるためです。

1
user23743

マルチスレッドコンテキストでの誤った共有を防ぐために、構造がキャッシュライン全体を使用することを確認するためにこれを使用するのを見てきました。デフォルトでゆるくパックされるオブジェクトが多数ある場合、メモリを節約し、キャッシュのパフォーマンスを改善してそれらをより密にパックすることができますが、通常、アライメントされていないメモリアクセスは物事を遅くするため、マイナス面があるかもしれません.

1
stonemetal

#pragma packが提供するデータの整合性を実現する他の方法があることに注意してください(たとえば、一部の人々は、ネットワーク経由で送信する必要がある構造に#pragma pack(1)を使用します)。たとえば、次のコードとその後の出力を参照してください。

#include <stdio.h>

struct a {
    char one;
    char two[2];
    char eight[8];
    char four[4];
};

struct b { 
    char one;
    short two;
    long int eight;
    int four;
};

int main(int argc, char** argv) {
    struct a twoa[2] = {}; 
    struct b twob[2] = {}; 
    printf("sizeof(struct a): %i, sizeof(struct b): %i\n", sizeof(struct a), sizeof(struct b));
    printf("sizeof(twoa): %i, sizeof(twob): %i\n", sizeof(twoa), sizeof(twob));
}

出力は次のとおりです。sizeof(struct a):15、sizeof(struct b):24 sizeof(twoa):30、sizeof(twob):48

構造体aのサイズはバイトカウントとまったく同じですが、構造体bにはパディングが追加されています(パディングの詳細については this を参照してください)。 #pragmaパックとは対照的にこれを行うことにより、「ワイヤー形式」を適切なタイプに変換する制御ができます。たとえば、「char two [2]」を「short int」などに変換します。

0
wangchow