プラグマを使用せずに C で構造パディングを無効にするにはどうすればよいですか?
6 に答える
これを行う標準的な方法はありません。標準では、パディングは実装の裁量で行うことができると規定されています。C99 6.7.2.1 Structure and union specifiers、段落 12 から:
構造体または共用体オブジェクトの各非ビット フィールド メンバーは、その型に適した実装定義の方法で整列されます。
そうは言っても、試すことができることがいくつかあります。
#pragmaコンパイラにパックしないように説得するために使用して、すでに割り引いている最初のもの。いずれにせよ、これは移植性がありません。他の実装固有の方法もありませんが、この機能が本当に必要な場合は必要になる可能性があるため、それらを確認する必要があります。
long long2 つ目は、すべての型の後にすべての型long、次にすべての型int、short最後に型というように、フィールドを最大から最小の順に並べることcharです。ほとんどの場合、アラインメント要件がより厳しいのはより大きな型であるため、これは通常は機能します。繰り返しますが、ポータブルではありません。
3 番目に、型を配列として定義しchar、アドレスをキャストして、パディングがないようにすることができます。ただし、変数が適切に配置されていないと速度が低下するアーキテクチャもあれば、惨めに失敗するアーキテクチャもあります (たとえば、BUS エラーを発生させてプロセスを終了するなど)。
その最後のものには、さらに説明があります。次の順序でフィールドを持つ構造があるとします。
char C; // one byte
int I; // two bytes
long L; // four bytes
パディングを使用すると、次のバイトになる可能性があります。
CxxxIIxxLLLL
xパディングはどこにありますか。
ただし、構造を次のように定義した場合:
typedef struct { char c[7]; } myType;
myType n;
あなたが得る:
CCCCCCC
次に、次のようなことができます。
int *pInt = &(n.c[1]);
int *pLng = &(n.c[3]);
int myInt = *pInt;
int myLong = *pLng;
あなたに与えるために:
CIILLLL
繰り返しますが、残念ながら、ポータブルではありません。
これらの「解決策」はすべて、コンパイラとその基礎となるデータ型について十分な知識を持っていることに依存しています。
pragma pack などのコンパイラ オプション以外は、できません。パディングは C 標準にあります。
次のように、最小の型を構造体の最後に宣言することで、常にパディングを減らすことができます。
struct _foo {
int a; /* No padding between a & b */
short b;
} foo;
struct _bar {
short b; /* 2 bytes of padding between a & b */
int a;
} bar;
4 バイト境界を持つ実装に関する注記
一部のアーキテクチャでは、位置合わせされていないデータの処理を要求された場合、CPU 自体が反対します。これを回避するために、コンパイラは複数のアライメントされた読み取りまたは書き込み命令を生成し、さまざまなビットをシフトおよび分割またはマージすることができます。整列されたデータ処理よりも 5 倍または 10 倍遅くなると合理的に予想できます。しかし、標準では、コンパイラがそれを行う準備をする必要はありません...パフォーマンスコストを考えると、十分な需要がありません. パディングの明示的な制御をサポートするコンパイラは、プラグマが非標準機能用に予約されているため、独自のプラグマを提供します。
パディングされていないデータを処理する必要がある場合は、独自のアクセス ルーチンを作成することを検討してください。アラインメントをあまり必要としない型 (char/int8_t を使用するなど) を試してみたいと思うかもしれませんが、構造体のサイズが 4 の倍数に切り上げられる可能性はあります。メモリ領域全体に対する独自のアクセスを実装する必要があります。
パディングなしの構造体が本当に必要な場合: 8 ビット バイトのみで構成される構造体またはクラスを使用して、short、int、long などの置換データ型を定義します。次に、置換データ型を使用して上位レベルの構造体を構成します。
C++ の演算子のオーバーロードは非常に便利ですが、C ではクラスの代わりに構造体を使用して同じ効果を得ることができます。以下のキャストと代入の実装は、CPU が整列されていない 32 ビット整数を処理できることを前提としていますが、他の実装ではより厳密な CPU に対応できます。
サンプルコードは次のとおりです。
#include <stdint.h>
#include <stdio.h>
class packable_int { public:
int8_t b[4];
operator int32_t () const { return *(int32_t*) b; }
void operator = ( int32_t n ) { *(int32_t*) b = n; }
};
struct SA {
int8_t c;
int32_t n;
} sa;
struct SB {
int8_t c;
packable_int n;
} sb;
int main () {
printf ( "sizeof sa %d\n", sizeof sa ); // sizeof sa 8
printf ( "sizeof sb %d\n", sizeof sb ); // sizeof sb 5
return 0;
}
コンパイラにパディングを行わせるか、#pragma を使用してパディングを行わないように指示するか、char 配列のようなバイトの束を使用するだけで、すべてのデータを自分で構築します (シフトしてバイトを追加します)。これは非常に非効率的ですが、バイトのレイアウトを正確に制御できます。ネットワークパケットを手動で準備することもありましたが、ほとんどの場合、それが標準であっても悪い考えです。