gcc结构中的内存对齐

问题描述 投票:12回答:6

我将应用程序移植到C中的ARM平台,该应用程序也在x86处理器上运行,并且必须向后兼容。

我现在有一些变量对齐的问题。我已经阅读了__attribute__((aligned(4),packed))的gcc手册我解释了所说的内容,因为struct的开头与4字节边界对齐,并且由于packed语句,内部保持不变。

最初我有这个,但偶尔它与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字节边界对齐,但由于字节顺序,结构的大小增加了大小从42到44个字节。这个大小是至关重要的,因为我们有其他依赖于结构为42字节的应用程序。

有些人可以向我描述如何执行我需要的操作。任何帮助深表感谢。

c gcc memory-management arm gcc4
6个回答
11
投票

如果你依赖于sizeof(yourstruct)是42字节,那么你将被一个不可移植的假设所困扰。你还没有说这是为了什么,但结构内容的字节顺序似乎也很重要,所以你也可能与x86不匹配。

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


5
投票

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

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

我想你有类似的东西:

read(fd, &obj, sizeof obj)

因为您不想读取属于不同数据的那2个填充字节,所以必须明确指定大小:

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)

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


2
投票

你的真正目标是什么?

如果要以特定格式处理文件或线路上的数据,您应该做的是编写一些编组/序列化例程,这些例程在编译器结构之间移动数据,表示您希望如何处理数据内部的数据。程序和一个char数组,用于处理数据在有线/文件上的显示方式。

然后,所有需要仔细处理并且可能具有特定于平台的代码的是编组例程。你可以编写一些讨厌的单元测试来确保封送的数据无论你在今天和将来可能需要移植到哪个平台,都能正确地进出结构。


1
投票

我一直在从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;

您可以使用它,它不需要任何特殊或聪明的代码。我写了很多与ARM通信的代码。结构是使事情有效的因素。 __attribute__ ((packed))是我的朋友。

如果你了解两者的情况,那么处于“伤害世界”的可能性是零。

最后,我不能为生活弄清楚你是如何得到42或44.Int是4我们的8字节(取决于编译器)。这使得数字在16 + 16 + 2 = 34或32 + 16 + 2 = 50 - 假设它是真正的包装。

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


0
投票

我猜想问题是42不能被4整除,所以如果你把这些结构中的几个连接起来就会失去对齐(例如为几个结构分配内存,用sizeof确定大小)。大小为44会根据您的要求强制对齐这些情况。但是,如果每个结构成员的内部偏移量保持不变,则可以将44字节结构视为42字节(只要您注意在正确的边界处对齐任何后续数据)。

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


-1
投票

当我使用linux时,我发现通过echo 3 > /proc/cpu/alignment它会向我发出警告,并修复对齐问题。这是一个解决方法,但它非常有助于找到结构未对齐的位置。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.