15

我正在用 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 字节边界对齐,但由于字节序,结构的大小在大小从 42 到 44 字节。这个大小很关键,因为我们有其他应用程序依赖于 42 字节的结构。

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

4

6 回答 6

16

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

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

于 2010-03-31T16:02:55.720 回答
6

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

在这种情况下,packing plus aligning at 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)

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

于 2010-03-31T16:04:40.033 回答
4

我一直在从 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 —— 假设它真的被打包了。

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

于 2018-08-30T20:51:10.237 回答
3

你真正的目标是什么?

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

然后,所有需要仔细处理并且可能具有平台特定代码的就是编组例程。并且您可以编写一些不错的单元测试,以确保无论您现在和将来必须移植到什么平台,封送数据都可以正确地进出结构。

于 2010-03-31T19:45:23.260 回答
0

我猜问题在于 42 不能被 4 整除,因此如果您将其中几个结构体背靠背放置(例如,为其中几个结构体分配内存,使用 确定大小sizeof),它们就会失去对齐。在这些情况下,大小为 44 会按照您的要求强制对齐。但是,如果每个结构成员的内部偏移量保持不变,您可以将 44 字节结构视为 42 字节(只要您注意在正确的边界对齐任何后续数据)。

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

于 2010-03-31T15:55:50.403 回答
-3

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

于 2010-04-01T14:47:15.467 回答