C语言 gcc 结构中的内存对齐

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2554229/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:01:07  来源:igfitidea点击:

memory alignment within gcc structs

cgccmemory-managementarmgcc4

提问by Mumbles

I am porting an application to an ARM platform in C, the application also runs on an x86 processor, and must be backward compatible.

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

I am now having some issues with variable alignment. I have read the gcc manual for __attribute__((aligned(4),packed))I interpret what is being said as the start of the struct is aligned to the 4 byte boundry and the inside remains untouched because of the packed statement.

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

originally I had this but occasionally it gets placed unaligned with the 4 byte boundary.

最初我有这个,但偶尔它会与 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;

so I change it to this.

所以我把它改成这个。

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;

The understand I stated earlier seems to be incorrect as both the struct is now aligned to a 4 byte boundary, and and the inside data is now aligned to a four byte boundary, but because of the endianess, the size of the struct has increased in size from 42 to 44 bytes. This size is critical as we have other applications that depend on the struct being 42 bytes.

我之前所说的理解似乎是不正确的,因为结构现在对齐到 4 字节边界,并且内部数据现在对齐到四字节边界,但是由于字节顺序,结构的大小增加了大小从 42 到 44 字节。这个大小很关键,因为我们有其他应用程序依赖于 42 字节的结构。

Could some describe to me how to perform the operation that I require. Any help is much appreciated.

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

采纳答案by crazyscot

If you're depending on sizeof(yourstruct)being 42 bytes, you're about to be bitten by a world of non-portable assumptions. You haven't said what this is for, but it seems likely that the endianness of the struct contents matters as well, so you may also have a mismatch with the x86 there too.

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

In this situation I think the only sure-fire way to cope is to use unsigned char[42]in the parts where it matters. Start by writing a precise specification of exactly what fields are where in this 42-byte block, and what endian, then use that definition to write some code to translate between that and a struct you can interact with. The code will likely be either all-at-once serialisation code (aka marshalling), or a bunch of getters and setters.

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

回答by crazyscot

This is one reason why reading whole structs instead of memberwise fails, and should be avoided.

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

In this case, packing plus aligning at 4 means there will be two bytes of padding. This happens because the size must be compatible for storing the type in an array with all items still aligned at 4.

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

I imagine you have something like:

我想你有这样的事情:

read(fd, &obj, sizeof obj)

Because you don't want to read those 2 padding bytes which belong to different data, you have to specify the size explicitly:

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

read(fd, &obj, 42)

Which you can keep maintainable:

您可以保持可维护性:

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

// ...

read(fd, &obj, obj.read_size)

Or, if you can't use some features of C++ in your C:

或者,如果您不能在 C 中使用 C++ 的某些功能:

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

// ...

read(fd, &obj, CHALLENGE_read_size)

At the next refactoring opportunity, I would strongly suggest you start reading each member individually, which can easily be encapsulated within a function.

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

回答by Michael Burr

What is your true goal?

你真正的目标是什么?

If it's to deal with data that's in a file or on the wire in a particular format what you should do is write up some marshaling/serialization routines that move the data between the compiler struct that represents how you want to deal with the data inside the program and a char array that deals with how the data looks on the wire/file.

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

Then all that needs to be dealt with carefully and possibly have platform specific code is the marshaling routines. And you can write some nice-n-nasty unit tests to ensure that the marshaled data gets to and from the struct properly no matter what platform you might have to port to today and in the future.

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

回答by Cat Sargent

I've been moving structures back and forth from Linux, Windows, Mac, C, Swift, Assembly, etc.

我一直在从 Linux、Windows、Mac、C、Swift、Assembly 等来回移动结构。

The problem is NOT that it can't be done, the problem is that you can't be lazy and must understand your tools.

问题不在于它不能完成,问题在于你不能偷懒,必须了解你的工具。

I don't see why you can't use:

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

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

You canuse it and it doesn't require any special or clever code. I write a LOT of code that communicates to ARM. Structures are what make things work. __attribute__ ((packed))is my friend.

可以使用它,它不需要任何特殊或巧妙的代码。我写了很多与 ARM 通信的代码。结构是使事情发挥作用的原因。__attribute__ ((packed))是我的朋友。

The odds of being in a "world of hurt" are nil if you understand what is going on with both.

如果您了解两者的情况,那么进入“伤害世界”的几率为零。

Finally, I can't for the life make out how you get 42 or 44. Int is either 4 or 8 bytes (depending on the compiler). That puts the number at either 16+16+2=34 or 32+16+2=50 -- assuming it is truly packed.

最后,我终生无法弄清楚你是如何得到 42 或 44 的。 Int 是 4 或 8 个字节(取决于编译器)。这使得数字为 16+16+2=34 或 32+16+2=50——假设它真的被打包了。

As I say, knowing your tools is part of your problem.

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

回答by Arkku

I would guess that the problem is that 42 isn't divisible by 4, and so they get out of alignment if you put several of these structs back to back (e.g. allocate memory for several of them, determining the size with sizeof). Having the size as 44 forces the alignment in these cases as you requested. However, if the internal offset of each struct member remains the same, you can treat the 44 byte struct as though it was 42 bytes (as long as you take care to align any following data at the correct boundary).

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

One trick to try might be putting bothof these structs inside a single union type and only use 42-byte version from within each such union.

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

回答by Mumbles

As I am using linux, I have found that by echo 3 > /proc/cpu/alignmentit will issue me with a warning, and fix the alignment issue. This is a work around but it is very helpful with locating where the structures are failing to be misaligned.

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