#pragma pack誰かがプリプロセッサステートメントが何をするのか、そしてもっと重要なことに、なぜそれを使いたいのかを私に説明できるかどうか疑問に思いました。
いくつかの洞察を提供するMSDNページをチェックしましたが、経験のある人からもっと多くのことを聞きたいと思っていました。私は以前にコードでそれを見たことがありますが、もうどこを見つけることができないようです。
#pragma pack誰かがプリプロセッサステートメントが何をするのか、そしてもっと重要なことに、なぜそれを使いたいのかを私に説明できるかどうか疑問に思いました。
いくつかの洞察を提供するMSDNページをチェックしましたが、経験のある人からもっと多くのことを聞きたいと思っていました。私は以前にコードでそれを見たことがありますが、もうどこを見つけることができないようです。
#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になります。
#pragmaポータブルではない(このコンパイラのみの場合のように)メッセージをコンパイラに送信するために使用されます。特定の警告を無効にしたり、構造体をパッキングしたりすることが一般的な理由です。エラーフラグをオンにして警告をコンパイルする場合は、特定の警告を無効にすることが特に役立ちます。
#pragma pack具体的には、パックされる構造体のメンバーを整列させてはならないことを示すために使用されます。ハードウェアへのメモリマップドインターフェイスがあり、さまざまな構造体メンバーが指す場所を正確に制御できる必要がある場合に便利です。ほとんどのマシンは整列されたデータの処理がはるかに高速であるため、これは特に速度の最適化としては適切ではありません。
後で元に戻すには、ラップイン#pragma pack(push,1)して#pragma pack(pop)
これは、構造内のオブジェクトを整列する境界をコンパイラーに指示します。たとえば、次のようなものがある場合:
struct foo {
char a;
int b;
};
通常の32ビットマシンでは、アクセス速度を最大化するために、aとの間に3バイトのパディングが必要であり、4バイトの境界に到達します(これがデフォルトで通常発生します)。bb
ただし、外部で定義された構造と一致させる必要がある場合は、コンパイラがその外部定義に従って構造を正確にレイアウトするようにします。この場合、コンパイラに、メンバー間にパディングを挿入しない#pragma pack(1)ように指示することができます。構造の定義にメンバー間のパディングが含まれている場合は、明示的に挿入します(たとえば、通常、または、などの名前のメンバーを使用します)。注文)。unusedNignoreN
データ要素(クラスや構造体のメンバーなど)は、通常、アクセス時間を改善するために、現世代のプロセッサのWORDまたはDWORD境界に配置されます。4で割り切れないアドレスでDWORDを取得するには、32ビットプロセッサで少なくとも1つの追加のCPUサイクルが必要です。したがって、たとえば3つのcharメンバーがchar a, b, c;ある場合、実際には6バイトまたは12バイトのストレージを使用する傾向があります。
#pragmaこれをオーバーライドして、アクセス速度を犠牲にして、または異なるコンパイラターゲット間で保存されたデータの一貫性を保つために、より効率的なスペース使用を実現できます。この16ビットから32ビットコードへの移行はとても楽しかったです。64ビットコードへの移植は、一部のコードで同じ種類の頭痛の種になると思います。
コンパイラーは、特定のプラットフォームで最大のパフォーマンスを達成するために、構造内のメンバーを整列させることができます。#pragma packディレクティブを使用すると、その配置を制御できます。通常、最適なパフォーマンスを得るには、デフォルトのままにしておく必要があります。構造をリモートマシンに渡す必要がある場合は、通常#pragma pack 1、不要な配置を除外するために使用します。
コンパイラは、特定のアーキテクチャでのパフォーマンス上の理由から、特定のバイト境界に構造体メンバーを配置する場合があります。これにより、メンバー間に未使用のパディングが残る場合があります。構造物のパッキングにより、部材が隣接するようになります。
これは、たとえば、特定のファイルまたは通信形式に準拠する構造が必要な場合に重要になることがあります。この場合、データがシーケンス内の特定の位置にある必要があります。ただし、このような使用法ではエンディアンの問題は処理されないため、使用しても移植性がない場合があります。
また、レジスタアクセスが直接アドレスではなく構造を介して行われるようにするために、UARTやUSBコントローラなどの一部のI/Oデバイスの内部レジスタ構造を正確にオーバーレイすることもできます。
マルチスレッドコンテキストでの誤った共有を防ぐために、構造体がキャッシュライン全体を使用することを確認するために人々がそれを使用するのを見てきました。デフォルトで緩くパックされるオブジェクトが多数ある場合は、メモリを節約し、キャッシュパフォーマンスを向上させて、オブジェクトをより密にパックできます。ただし、アラインされていないメモリアクセスは通常、処理速度を低下させるため、マイナス面が生じる可能性があります。
これは、レジスタの順序と配置に厳しい要件があるハードウェア(メモリマップドデバイスなど)にコーディングする場合にのみ使用することをお勧めします。
ただし、これはその目的を達成するためのかなり鈍いツールのように見えます。より良いアプローチは、このプラグマをいじくり回すのではなく、アセンブラでミニドライバをコーディングし、それにC呼び出しインターフェイスを与えることです。
私は以前にコードでそれを使用しましたが、レガシーコードとのインターフェースのためだけです。これはMacOSX Cocoaアプリケーションであり、以前のCarbonバージョン(元のM68k System 6.5バージョンとの下位互換性がありました...ご存知のとおり)から設定ファイルをロードする必要がありました。元のバージョンの設定ファイルは、構成構造のバイナリダンプであり、#pragma pack(1)余分なスペースを占有してジャンク(つまり、構造内にあるパディングバイト)を節約するためにを使用していました。
コードの元の作成者は、#pragma pack(1)プロセス間通信でメッセージとして使用される構造を格納するためにも使用されていました。ここでの理由は、パディングサイズが不明または変更される可能性を回避するためだと思います。コードは、最初からバイト数をカウントすることによってメッセージ構造体の特定の部分を調べることがあるためです(ewww)。
#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にはパディングが追加されています(パディングの詳細については、これを参照してください)。#pragmaパックとは対照的にこれを行うことにより、「ワイヤ形式」を適切なタイプに変換する制御を行うことができます。たとえば、「chartwo[2]」を「shortint」などに変換します。
なぜそれを使いたいのですか?
構造のメモリを減らすため
なぜそれを使うべきではないのですか?