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

gcc结构中的内存对齐

  •  12
  • Mumbles  · 技术社区  · 16 年前

    我正在将一个应用程序移植到c语言的arm平台,该应用程序也运行在x86处理器上,并且必须向后兼容。

    我现在对变量对齐有一些问题。我已经阅读了GCC手册 __attribute__((aligned(4),packed)) 我将所说的解释为结构的开头与4字节的边界对齐,内部由于压缩语句而保持不变。

    最初我有这个,但偶尔它被放置与4字节边界不对齐。

    typedef struct  
    {  
     unsigned int code;  
     unsigned int length;  
     unsigned int seq;  
     unsigned int request;  
     unsigned char nonce[16];  
     unsigned short  crc;  
    } __attribute__((packed)) CHALLENGE;
    

    所以我改成这个。

    typedef struct  
    {  
     unsigned int code;  
     unsigned int length;  
     unsigned int seq;  
     unsigned int request;  
     unsigned char nonce[16];  
     unsigned short  crc;  
    } __attribute__((aligned(4),packed)) CHALLENGE;
    

    我前面所说的理解似乎是不正确的,因为两个结构现在都与4字节边界对齐,内部数据现在与4字节边界对齐,但是由于endianess,结构的大小从42字节增加到44字节。这个大小非常重要,因为我们有其他依赖于42字节的结构的应用程序。

    有人能给我描述一下如何执行我所要求的操作吗?任何帮助都非常感谢。

    6 回复  |  直到 8 年前
        1
  •  10
  •   crazyscot    16 年前

    如果你依赖 sizeof(yourstruct) 作为42字节,您将被一个不可移植的假设世界所困扰。您还没有说明这是为了什么,但结构内容的结束性似乎也很重要,因此您可能也与x86不匹配。

    在这种情况下,我认为唯一可靠的解决办法就是 unsigned char[42] 在重要的地方。首先编写一个精确的规范,说明在这个42字节的块中,哪些字段在哪里,以及什么endian,然后使用这个定义编写一些代码,以便在它和可以交互的结构之间进行转换。代码可能是一次序列化代码(也称为编组),或者是一堆getter和setter。

        2
  •  5
  •   Roger Pate    16 年前

    这就是为什么读取整个结构而不是成员级失败的原因之一,应该避免。

    在这种情况下,打包加上在4处对齐意味着将有两个字节的填充。发生这种情况是因为大小必须兼容,以便将类型存储在数组中,并且所有项仍在4处对齐。

    我想你有点像:

    read(fd, &obj, sizeof obj)
    

    因为您不想读取属于不同数据的这两个填充字节,所以必须显式指定大小:

    read(fd, &obj, 42)
    

    可以保持可维护性:

    typedef struct {
      //...
      enum { read_size = 42 };
    } __attribute__((aligned(4),packed)) CHALLENGE;
    
    // ...
    
    read(fd, &obj, obj.read_size)
    

    或者,如果你不能在C++中使用C++的一些特性:

    typedef struct {
      //...
    } __attribute__((aligned(4),packed)) CHALLENGE;
    enum { CHALLENGE_read_size = 42 };
    
    // ...
    
    read(fd, &obj, CHALLENGE_read_size)
    

    在下一次重构时,我强烈建议您开始单独阅读每个成员,这些成员可以很容易地封装在函数中。

        3
  •  2
  •   Michael Burr    16 年前

    你真正的目标是什么?

    如果要以特定格式处理文件或线路上的数据,应该编写一些封送处理/序列化例程,将数据在表示要如何处理程序内数据的编译器结构和处理h数据在导线/文件上的外观。

    然后,所有需要小心处理并且可能有特定于平台的代码的就是封送处理例程。而且,您可以编写一些非常糟糕的单元测试,以确保封送处理的数据能够正确地往返于结构,而不管您今天和将来可能要移植到哪个平台。

        4
  •  0
  •   Arkku    16 年前

    我猜问题是42不能被4整除,所以如果你把这些结构中的几个背对背地放在一起,它们就会失去对齐(例如,为其中的几个分配内存,用 sizeof )在这些情况下,如果尺寸为44,则会按您的要求强制对齐。但是,如果每个结构成员的内部偏移量保持不变,则可以将44字节的结构视为42字节(只要注意在正确的边界处对齐以下任何数据)。

    一个尝试的技巧可能是 二者都 在一个联合类型中使用这些结构,并且在每个这样的联合中只使用42字节版本。

        5
  •  0
  •   Cat Sargent    8 年前

    我一直在从linux、windows、mac、c、swift、assembly等移动结构。

    问题不是做不到,问题是你不能懒惰,必须理解你的工具。

    我不明白你为什么不能用:

    typedef struct  
    {  
     unsigned int code;  
     unsigned int length;  
     unsigned int seq;  
     unsigned int request;  
     unsigned char nonce[16];  
     unsigned short  crc;  
    } __attribute__((packed)) CHALLENGE;
    

    如果你了解这两种情况的话,身处“受伤世界”的几率是零。

    最后,我一辈子都搞不懂你是怎么得到42或44的。int是我们8个字节中的4个(取决于编译器)。这将数字设为16+16+2=34或32+16+2=50——假设它是真压缩的。

    正如我所说,了解你的工具是你问题的一部分。

        6
  •  -1
  •   Mumbles    16 年前

    当我使用Linux时,我发现 echo 3 > /proc/cpu/alignment 它将向我发出警告,并修复对齐问题。这是一个工作,但它是非常有助于定位的地方,结构是没有错位。