このページはWritingPortableDriversセクションの一部です。
データアラインメント
原文: DataAlignment
gccコンパイラは通常、実行速度を速くさせるために、構造体の個々のフィールドをバイト境界に合わせます。 例として、次のコードとその実行結果を見てください。
1 #include <stdio.h>
2 #include <stddef.h>
3
4 struct foo {
5 char a;
6 short b;
7 int c;
8 };
9
10 #define OFFSET_A offsetof(struct foo, a)
11 #define OFFSET_B offsetof(struct foo, b)
12 #define OFFSET_C offsetof(struct foo, c)
13
14 int main ()
15 {
16 printf ("offset A = %d\n", OFFSET_A);
17 printf ("offset B = %d\n", OFFSET_B);
18 printf ("offset C = %d\n", OFFSET_C);
19 return 0;
20 }
上記のプログラムを実行すると、次のように出力します。
offset A = 0 offset B = 2 offset C = 4
この出力は、コンパイラが構造体struct fooのフィールドbとcを偶数バイト境界に合わせた ことを示しています。これは構造体のデータをメモリ上にオーバーレイするときに問題となります。一般的に、 ドライバで使用されているデータ構造は、個々のフィールドが偶数バイトとなるようにパディングしません。 そのため、gccの属性(packed)を使用し、構造体の中に「メモリホール」を作らないようにコンパイラに 指示します。
次に示すように、構造体struct fooに対してpacked属性を使用するように修正した場合、
プログラムの出力は次のように変わります。
offset A = 0 offset B = 1 offset C = 3
これで構造体の中にメモリホールはなくなりました。
このpacked属性は、前述の構造体で使用したように構造体全体をパックするために使用することもできますし、 構造体の中の特定のいくつかのフィールドだけをパックすることにも使用できます。
たとえば、struct usb_ctrlrequestはinclude/usb.hの中で次のように定義されています。
このデータ構造は、構造体全体がパックされることを保証しているので、データを直接USBコネクションに 書き込むことができます。
しかし、次に示すstruct usb_endpoint_descriptorの定義は次のようになっています。
1 struct usb_endpoint_descriptor {
2 __u8 bLength __attribute__((packed));
3 __u8 bDescriptorType __attribute__((packed));
4 __u8 bEndpointAddress __attribute__((packed));
5 __u8 bmAttributes __attribute__((packed));
6 __le16 wMaxPacketSize __attribute__((packed));
7 __u8 bInterval __attribute__((packed));
8 __u8 bRefresh __attribute__((packed));
9 __u8 bSynchAddress __attribute__((packed));
10 unsigned char *extra; /* Extra descriptors */
11 int extralen;
12 };
このデータ構造は、構造体の最初の部分はパックされていて、USBコネクションから直接データを読み込むのに 使用できますが、extraフィールドとextralenフィールドは高速にアクセスできるように、コンパイラがバイト境界を 合わせることがあります。