这可能是一个对话记忆问题吗?

时间:2017-11-21 12:58:05

标签: c memory alignment

我有一个映射到字节数组的结构。但我发现似乎并非所有字段都已正确映射。我想知道这是一个对齐问题还是一个错误。

struct _sg64_struct
{
  SG64_PCSC_TLV_HEADER header; /* This is 2 bytes */
  gint8 id_perso;
  gint8 status;
  gint8 fare_type;  
  gint16 fare_zone;     
  gint8 support_type;   
  gchar loginPerso[15];
};

我将此结构映射到此字节:

41150002000000000E2020202020202020202020202020

但我发现support_type得到0x0E值而不是00应该是。如果我们映射它

    struct _sg64_struct
{
  SG64_PCSC_TLV_HEADER header; 4115
  gint8 id_perso; 00
  gint8 status;   02
  gint8 fare_type;  00
  gint16 fare_zone;     0000
  gint8 support_type;   00
  gchar loginPerso[15]; E2020202020202020202020202020
};

但是当我用0E说support_type时,我得到了。奇怪的是,如果我将fare_zone映射为两个字节就可以了。

struct _sg64_struct
{
  SG64_PCSC_TLV_HEADER header; /* This is 2 bytes */
  gint8 id_perso;
  gint8 status;
  gint8 fare_type;  
  gint8 fare_zone[2];   
  gint8 support_type;   
  gchar loginPerso[15];
};

这种结构有效。但问题是为什么?我们可以信任编译器类型吗?

2 个答案:

答案 0 :(得分:3)

它不起作用,因为结构内部有填充物。这是一个非常糟糕的做法。假设结构成员的偏移量只是所有先前成员的总和。尺寸错了。

答案 1 :(得分:2)

结构中每个成员的类型通常具有默认对齐,这意味着除非程序员另有要求,否则它将在预定边界上对齐。

  

gint8

     

typedef signed char gint8;

     

在所有平台上保证为8位的有符号整数。

  

gint16

     

typedef签名短gint16;

     

所有平台上的无符号整数保证为8位。

如果您的结构中有gint16类型fare_zone,则编译器会将struct _sg64_struct填充为1个字节,以对齐fare_zone 。< / p>

因此,当fare_zone类型为gint16时,您的结构会发生这种情况:

struct _sg64_struct
{
  SG64_PCSC_TLV_HEADER header;  // 2 bytes  4115
  gint8 id_perso;               // 1 byte   00 
  gint8 status;                 // 1 byte   02
  gint8 fare_type;              // 1 byte   00
  gchar pad[1];         <------ // 1 byte   00 (compiler is padding 1 byte to align fare_zone, as short type are 2-byte aligned)
  gint16 fare_zone;             // 2 byte   0000   
  gint8 support_type;           // 1 byte   0E
  gchar loginPerso[15];         // 15 byte  2020202020202020202020202020
};

这就是support_type得到0x0E值而不是00的原因。

当您的结构中有gint8类型的fare_zone时,编译器的填充大小为struct _sg64_struct,其中1个字节为对齐边界并执行尾部填充

struct _sg64_struct
{
  SG64_PCSC_TLV_HEADER header; // 2bytes     4115
  gint8 id_perso;              // 1 byte     00
  gint8 status;                // 1 byte     02
  gint8 fare_type;             // 1 byte     00
  gint8 fare_zone[2];          // 2 byte     0000 (no alignment required as the char types are 1 byte aligned)
  gint8 support_type;          // 1 byte     00
  gchar loginPerso[15];        // 15 byte    0E2020202020202020202020202020
  gchar pad[1];        <------ // Compiler is padding 1 byte to alignment boundary of structure
};

这就是这种结构有效的原因。

我建议不要使用字节序列映射结构,因为结构成员的对齐可能会因编译器和底层平台而异。

附加说明:

每个现代编译器都会根据体系结构自动使用数据结构填充。有些编译器甚至支持警告标志-Wpadded,它会生成有关结构填充的有用警告。这些警告有助于程序员在需要更高效的数据结构布局时进行手动维护。

  

<强> -Wpadded

     

如果填充包含在结构中,则发出警告,要么对齐元素   结构或对齐整​​个结构。有时这个   发生可以重新排列结构的字段   减少填充,使结构更小。

因此,如果您的编译器支持警告标志-Wpadded,请尝试使用它编译代码。这将帮助您理解编译器包含在结构中的填充。