2013-05-15 8 views
6

私の好奇心のために、私の構造体の各バイトを表示するプログラムを作成しました。ここでは、コードは次のようになります。64ビットマシン上のメモリ内の構造体表現

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdint.h> 
#include <limits.h> 

#define MAX_INT 2147483647 
#define MAX_LONG 9223372036854775807 

typedef struct _serialize_test{ 
    char a; 
    unsigned int b; 
    char ab; 
    unsigned long long int c; 
}serialize_test_t; 


int main(int argc, char**argv){ 
    serialize_test_t *t; 
    t = malloc(sizeof(serialize_test_t)); 
    t->a = 'A'; 
    t->ab = 'N'; 
    t->b = MAX_INT; 
    t->c = MAX_LONG; 

    printf("%x %x %x %x %d %d\n", t->a, t->b, t->ab, t->c, sizeof(serialize_test_t), sizeof(unsigned long long int)); 

    char *ptr = (char *)t; 

    int i; 
    for (i=0; i < sizeof(serialize_test_t) - 1; i++){ 
     printf("%x = %x\n", ptr + i, *(ptr + i)); 
    } 

    return 0; 
} 

、ここでは出力されます:

41 7fffffff 4e ffffffff 24 8 
26b2010 = 41 
26b2011 = 0 
26b2012 = 0 
26b2013 = 0 
26b2014 = ffffffff 
26b2015 = ffffffff 
26b2016 = ffffffff 
26b2017 = 7f 
26b2018 = 4e 
26b2019 = 0 
26b201a = 0 
26b201b = 0 
26b201c = 0 
26b201d = 0 
26b201e = 0 
26b201f = 0 
26b2020 = ffffffff 
26b2021 = ffffffff 
26b2022 = ffffffff 
26b2023 = ffffffff 
26b2024 = ffffffff 
26b2025 = ffffffff 
26b2026 = ffffffff 

そして、ここでは質問です: sizeof(long long int) is 8あれば、なぜsizeof(serialize_test_t) is 24の代わりに、32 - 私は常に構造体の大きさがあると思いました例えば、8(バイト)* 4(フィールド)= 32(バイト) - デフォルトでは、プラグマパックのディレクティブはありません。

また、構造体をchar *にキャストすると、メモリからの値のオフセットが8バイトではないことが出力からわかります。私に手がかりを与えることができますか?または、これはコンパイラの最適化の一部ですか?

+3

あなたの前提は間違っています。スタンダードはパッキングやパッ​​ディングについては、それ以外には何も言わない。 –

+0

パッディング要件はCやアーチ依存ではありませんが、ABIに依存すると、x86_64 ABIに固有の回答が得られる可能性があります.2つの主要なもの、win64とSystem V(その他すべて)はかなり似ています。 –

+0

標準では、構造体のパディングに関する1つのことが述べられています。構造体の最初の要素の前には何も存在しません。パディングがあるかどうかと、それが最初の要素の前のものと異なるかどうかはコンパイラによって異なります。しかし、構造体型 'struct X x;'の場合、 'x'のアドレスは' x'の最初の要素のアドレスでもあります(ただし、アドレス型は異なります)。 –

答えて

4

SPARC、Intel [34] 86、または68020以降のMotorolaチップのような現代の32ビットマシンでは、各データは通常、 "self-aligned"でなければなりません。その型サイズの倍数したがって、32ビット型は32ビット境界で開始し、16ビット型は16ビット境界で開始し、8ビット型はどこからでも始めることができます。構造体/配列/共用体型は最も制限の厳しいメンバーの配列を持ちます。

構造の合計サイズが、このようにし

typedef struct _serialize_test{ 

    char a;//size 1 byte 

    padding for 3 Byte; 

    unsigned int b;//size 4 Byte 

    char ab;//size 1 Byte again 

    padding of 7 byte; 

    unsigned long long int c;//size 8 byte 

}serialize_test_t; 

のようになります。8バイトの最終的な構造として起こってpacking.Inにあなたのケースを依存します最初の2つと最後の2つは、適切かつ合計整列されていますサイズは最大24に達します。

+1

M68000、M68008、M68010はM68020とは異なるものですか? –

+0

@JonathanLeffler - > 68010,68020改善(32ビット算術論理演算ユニット(ALU))、以前の68000および68010プロセッサは、ワード整列の場合にのみワード(16ビット)およびロングワード(32ビット)データにアクセスできました。 68020にはデータアクセスに関する整合性の制限がありませんでした。 –

+0

しかし、この特定のケースでは、OPの投稿と同じ方法で構造体メンバを整列させる68000コンパイラはありませんか?この特定の例では16ビットのアクセスがないためです。 – Lundin

0

構造体(この場合8)ワードサイズの倍数になるようにパディングが一般添加する

したがって、最初の2つのフィールドは、1つの8バイトのチャンクです。 3番目のフィールドは別の8バイトチャンクにあり、最後は8バイトチャンクにあります。合計24バイトです。

char 
padding 
padding 
padding 
unsigned int 
unsigned int 
unsigned int 
unsigned int 
char       // Word Boundary 
padding 
padding 
padding 
padding 
padding 
padding 
padding 
unsigned long long int   // Word Boundary 
unsigned long long int 
unsigned long long int 
unsigned long long int 
unsigned long long int 
unsigned long long int 
unsigned long long int 
unsigned long long int 
0

アライメントと関連があります。

構造体のサイズは、最大の型に丸められず、フィールドによって乗算されません。バイトは、それぞれの種類によってそれぞれを揃えています http://en.wikipedia.org/wiki/Data_structure_alignment#Architectures

アライメントはタイプがそう、そのサイズの倍数であるメモリアドレスに表示されなければならないことで動作します:

charは1バイトアラインであるので、メモリのどこにでも1の倍数(どこでも)で表示できます。

unsigned int型はcharがどこでも可能4.

の倍数のアドレスに開始する必要があります。 8.

の倍数になるように

、その後、長い長いニーズあなたはアドレスを見ている場合、これがケースです。

2

コンパイラによって選択された配置に依存します。しかし、あなたが合理的に次のデフォルトを期待することができます。

typedef struct _serialize_test{ 
    char a;      // Requires 1-byte alignment 
    unsigned int b;    // Requires 4-byte alignment 
    char ab;      // Requires 1-byte alignment 
    unsigned long long int c;  // Requires 4- or 8-byte alignment, depending on native register size 
}serialize_test_t; 

を上記の要件を考えると、最初のフィールドは、ゼロオフセットされます。

フィールドbは、オフセット4(3バイトのパディング後)から開始します。

次のフィールドはオフセット8から始まります(パディングは不要です)。

次のフィールドは、オフセット12(32ビット)または16(64ビット)(別の3または7バイトのパディング後)から開始します。

プラットフォーム上のlong longの配置要件に応じて、合計サイズは20または24になります。

GCCを使用すると、任意の特定のメンバーのオフセットを識別するために使用できるoffsetof機能を持っている、またはあなたが1を自分で定義することができます。基本的に架空のベースを使用してアドレスの違いを使用してオフセットを計算

// modulo errors in parentheses... 
#define offsetof(TYPE,MEMBER) (int)((char *)&((TYPE *)0)->MEMBER - (char *)((TYPE *)0)) 

を集約タイプのアドレス。

0

コンパイラは、構造体メンバの個々のアライメントを1つずつ心配しています。それは全体として構造体について考えることはありません。バイナリレベルでは構造体が存在しないため、特定のアドレスオフセットに割り当てられた個々の変数のチャンクだけがオフセットされるためです。 "struct round-up"のようなものはありません。コンパイラは、structメンバーが適切に整列されていれば、structの大きさについてはあまり気にすることができませんでした。

C標準では、パディングの方法については何も言わず、コンパイラは構造体の先頭にパディングバイトを追加することはできません。それ以外にも、コンパイラは任意の数のパディングバイトを構造体の任意の場所に自由に追加できます。これは999バイトのパディングであり、まだ標準に準拠しています。

したがって、コンパイラは構造体を調べて、次のように見ます:ここではchar型ですが、整列が必要です。この場合、CPUはおそらく32ビットアクセス、すなわち4バイト整列を扱うことができる。これは、3つのパディングバイトだけを追加するためです。

次に、それは32ビットintをスポットし、アラインメントは必要なく、そのまま残します。その後、別のchar、3パディングバイト、64ビットint、アラインメントは必要ありません。

+0

コンパイラが「全体としての構造体」とみなす場所は1つあります。これは、構造体の配列が割り当てられたときに構造体が正しく揃うようにすることです。 'unsigned long long'の後にもう一つの' char d; 'フィールドがあったとします。構造体は8バイト長の倍数になります( 'unsigned long long'は8バイト整列する必要があると仮定します)。仮定的な' d'の後に7つのパディングバイトがあります。 –

+0

@JonathanLefflerそれは本当ですが、その場合は構造体よりも配列の性質に関連しています。文字の配列は、理論的には、整列の理由からメンバー間で同じパディングを得ることができます。私は、Cの標準の中にあるものが配列の内部でそのような埋め込みを許可しないかどうか覚えていません。 – Lundin

関連する問題