ほとんどのコンパイラは、データ値をプラットフォームのワード サイズまたはデータ型のサイズのいずれか小さい方に自動的に揃えます。コンシューマおよびエンタープライズ プロセッサの大多数は、32 ビット ワード サイズを使用します。(64 ビット システムでも通常はネイティブ ワード サイズとして 32 ビットを使用します)
そのため、構造体のメンバーの順序付けによって、メモリが浪費される可能性があります。あなたの特定のケースでは、大丈夫です。使用されたメモリの実際のフットプリントをコメントに追加します。
typedef struct structc_tag{
char c; // 1 byte
// 3 bytes (padding)
double d; // 8 bytes
int s; // 4 bytes
} structc_t; // total: 16 bytes
このルールは構造体にも適用されるため、構造体を並べ替えて最小のフィールドが最後になるようにしても、構造体は同じサイズ (16 バイト) のままです。
typedef struct structc_tag{
double d; // 8 bytes
int s; // 4 bytes
char c; // 1 byte
// 3 bytes (padding)
} structc_t; // total: 16 bytes
4 バイト未満のフィールドをさらに宣言する場合、それらをサイズ別にグループ化すると、メモリの削減が見られる場合があります。例えば:
typedef struct structc_tag{
double d1; // 8 bytes
double d2; // 8 bytes
double d3; // 8 bytes
int s1; // 4 bytes
int s2; // 4 bytes
int s3; // 4 bytes
short s4; // 2 bytes
short s5; // 2 bytes
short s6; // 2 bytes
char c1; // 1 byte
char c2; // 1 byte
char c3; // 1 byte
// 3 bytes (padding)
} structc_t; // total: 48 bytes
愚かな構造体を宣言すると、コンパイラが要素を並べ替えない限り、多くのメモリを浪費する可能性があります (一般的に、明示的に指示されない限り、そうはなりません)。
typedef struct structc_tag{
int s1; // 4 bytes
char c1; // 1 byte
// 3 bytes (padding)
int s2; // 4 bytes
char c2; // 1 byte
// 3 bytes (padding)
int s3; // 4 bytes
char c3; // 1 byte
// 3 bytes (padding)
} structc_t; // total: 24 bytes
// (9 bytes wasted, or 38%)
// (optimal size: 16 bytes (1 byte wasted))
double は 32 ビットより大きいため、最初のセクションの規則に従って、32 ビットで整列されます。アラインメントを変更するコンパイラ オプションについて誰かが言及し、デフォルトのコンパイラ オプションは 32 ビット システムと 64 ビット システムで異なると述べましたが、これも有効です。したがって、double に関する本当の答えは、プラットフォームとコンパイラに依存するということです。
メモリのパフォーマンスはワードによって制御されます。メモリからのロードは、データの配置に応じて段階的に行われます。データが 1 ワードをカバーする場合 (つまり、ワードでアラインされている場合)、そのワードのみをロードする必要があります。正しく配置されていない場合 (つまり、0x2 の int)、プロセッサはその値を正しく読み取るために 2 ワードをロードする必要があります。同じことが double にも当てはまり、通常は 2 ワードを使用しますが、位置合わせが正しくない場合は 3 を使用します。64 ビット量のネイティブ ロードが可能な 64 ビット システムでは、適切に位置合わせされている場合、32 ビット システムで 32 ビット int のように動作します。の場合、1 回のロードでフェッチできますが、それ以外の場合は 2 回必要になります。