代码之家  ›  专栏  ›  技术社区  ›  bialix

长对齐问题(MSVC与GCC)

  •  1
  • bialix  · 技术社区  · 16 年前

    下面是用于说明的代码:

    #include <stdio.h>
    #include <string.h>
    
    typedef struct {
        long long      a;
        int            b;
    } S1;
    
    typedef struct {
        long           a;
        int            b;
    } S2;
    
    S1 s1, s2;
    
    int main()
    {
        printf("%d %d\n", sizeof(S1), sizeof(S2));
    
        memset(&s1, 0xFF, sizeof(S1));
        memset(&s2, 0x00, sizeof(S1));
    
        s1.a = 0LL; s1.b = 0;
    
        if (0 == memcmp(&s1, &s2, sizeof(S1)))
            printf("Equal\n");
        else
            printf("Not equal\n");
    
        return 0;
    }
    

    此代码与MSVC 2003@Windows一起生成以下输出:

    16 8
    Not equal
    

    12 8
    Equal
    

    我是否正确理解MSVC使用最大本机类型(long-long)的大小来确定与结构的对齐?

    有人能给我一些建议吗?我该如何修改我的代码,使其对这种奇怪的对齐问题更加健壮?在我的实际代码中,我通过泛型指针处理结构数组来执行memset/memcmp,我通常不知道确切的类型,我只有sizeof(struct)值。

    6 回复  |  直到 16 年前
        1
  •  4
  •   eel ghEEz    16 年前

    单元测试的预期是错误的。它(或它测试的代码)不应该逐字节扫描结构的缓冲区。对于字节精确数据,代码应该在堆栈或堆上显式地创建字节缓冲区,并用每个成员的摘录填充它。通过对整数值使用右移位操作并通过字节类型(如(无符号字符)强制转换结果,可以以与CPU端号无关的方式获得提取。

    顺便说一句,你的代码片段写过s2。你可以通过改变这个来解决这个问题

    memset(&s2, 0x00, sizeof(S1));
    
    s1.a = 0LL; s1.b = 0;
    
    if (0 == memcmp(&s1, &s2, sizeof(S1)))
    

    memset(&s2, 0x00, sizeof(S2));
    
    s1.a = 0LL; s1.b = 0;
    
    if (0 == memcmp(&s1, &s2, sizeof(S2)))
    

    但结果在技术上是“未定义的”,因为结构中的成员对齐是特定于编译器的。

        2
  •  2
  •   dirkgently    16 年前

    请注意,ISO C标准要求任何给定结构或联合类型的对齐方式至少为相关结构或联合所有成员对齐方式最低公倍数的完美倍数。

    此外,这通常会引入填充元素(即填充字节以对齐结构)。你可以使用 #pragma packed . 笔记 #布拉格马

    参考资料: Here GCC关于结构调整。 MSDN

        3
  •  1
  •   Lyndsey Ferguson    16 年前

    我们所做的是使用#pragma pack指定对象的大小:

    #pragma pack(push, 2)
    
    typedef struct {
        long long      a;
        int            b;
    } S1;
    
    typedef struct {
        long           a;
        int            b;
    } S2;
    
    #pragma pack(pop)
    

        4
  •  1
  •   Michael Burr    16 年前

    请注意,这不是一个“奇怪”的对齐问题。MSVC选择确保结构在64位边界上对齐,因为它有一个64位成员,所以它在结构的末尾添加了一些填充,以确保这些对象的数组中的每个元素都正确对齐。事实上,我很惊讶GCC没有这样做。

        5
  •  1
  •   Charlotte    11 年前

    #ifdef _MSC_VER
    #pragma pack(push, 16)
    #endif
    
    /* your struct defs */
    
    #ifdef _MSC_VER
    #pragma pack(pop)
    #endif
    

    给出强制对齐的编译器指令的步骤

    或者进入项目选项并更改默认结构对齐方式[在代码生成下]

        6
  •  0
  •   HUAGHAGUAH    16 年前

    请注意,显式填充到64位对齐只会隐藏问题。如果您开始天真地嵌套结构,它就会回来,因为编译器仍然会对内部结构的自然对齐方式产生分歧。