C:使用64位对齐起始地址定义数组的便携式方法?
对于在各种/未知架构/编译器(8/16/32/64位)上编译的代码,必须定义全局MEMPOOL数组:
uint8_t mempool[SIZE];
此mempool用于存储不同结构类型的对象,例如:
typedef struct Meta_t {
uint16_t size;
struct Meta_t *next;
//and more
}
由于结构对象始终具有要与最大可能的边界保持一致,例如64字节必须确保在Mempool内部的那些结构对象之间添加填充字节:
struct Meta_t* obj = (struct Meta_t*) mempool[123] + padding;
这意味着如果结构对象在不对准的地址上启动对齐陷阱。
这在我的代码中已经很好。但是,我仍在寻找一种便携式方式来对齐Mempool Start地址。因为没有这些,就必须在数组启动地址和mempool内部第一个结构的地址之间插入填充字节。
到目前为止,我发现的唯一方法是将MEMPOOL定义在联合内部内部,并与另一个变量相结合,该变量无论如何都会由编译器对齐,但这应该是不可便携的。
不幸的是,对于嵌入式平台,我的代码还与ANSI C90编译器一起编译。实际上,我无法猜测哪些编译器完全使用。因此,我正在寻找一种绝对便携式的解决方案,我想任何类型的预处理器指令或编译器特定属性或语言功能无法使用C90之后
For code that is compiled on various/unknown architectures/compilers (8/16/32/64-bit) a global mempool array has to be defined:
uint8_t mempool[SIZE];
This mempool is used to store objects of different structure types e.g.:
typedef struct Meta_t {
uint16_t size;
struct Meta_t *next;
//and more
}
Since structure objects always have to be aligned to the largest possible boundary e.g. 64-byte it has to be ensured that padding bytes are added between those structure objects inside the mempool:
struct Meta_t* obj = (struct Meta_t*) mempool[123] + padding;
Meaning if a structure object would start on a not aligned address, the access to this would cause an alignment trap.
This works already well in my code. But I'm still searching for a portable way for aligning the mempool start address as well. Because without that, padding bytes have to be inserted already between the array start address and the address of the first structure inside the mempool.
The only way I have discovered so far is by defining the mempool inside a union together with another variable that will be aligned by the compiler anyways, but this is supposed be not portable.
Unfortunately for embedded platforms my code is also compiled with ANSI C90 compilers. In fact I cannot make any guess what compilers are exactly used. Because of this I'm searching for an absolutely portable solution and I guess any kind of preprocessor directives or compiler specific attributes or language features that were added after C90 cannot be used
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您可以使用
_alignas
,它是 C11标准,以强制特定的对齐方式。You can use
_Alignas
, which is part of the C11 standard, to force a particular alignment.(struct meta_t*)mempool
将导致不确定的行为,原因是比对齐更多 - 这也是严格的违法行为。最好的解决方案可能是创建一个
Union
,例如:这同时解决了对齐和指针混音问题,并在C90中起作用。
现在,如果我们执行
*(thing)mempool
,那么这是明确的定义,因为此(lvalue)访问是通过包含uint8_t
数组中的uint> uint8_t
数组来完成的。在Union
成员之间的类型punsing在C中也定义了。(C ++中不存在解决方案。)(struct Meta_t*) mempool
will lead to undefined behavior for more reasons than alignment - it's also a strict aliasing violation.The best solution might be to create a
union
such as this:This solves both the alignment and the pointer aliasing problems and works in C90.
Now if we do
*(thing)mempool
then this is well-defined since this (lvalue) access is done through a union type that includes anuint8_t
array among its members. And type punning betweenunion
members is also well-defined in C. (No solution exists in C++.)不幸的是,这个...
...与此相结合...
内容...除非您预先知道所有结构可以使用内存池的类型。即使您现在采用的方法也不严格符合C90,因为没有严格结合的方法来确定地址的对齐方式,以便计算需要多少填充。*(您可能假设您可以将其转换为整数并查看最不重要的位,但是C不能保证您可以以这种方式确定任何有关对齐方式的方法。)
在实践中,有很多事情可以在尽管不严格符合C语言规范,但目标环境范围很广。将指针转换为整数作为机器地址的结果,以便将其用于对齐计算是一个明智的选择。为了适当地对准一个声明的数组,这将是:
对于一系列非常大的C实现,这不仅可以确保池本身适当地对齐,以供严格结合的C90客户端使用,而且还可以方便地将池分为分配器可以绘制的对齐块。另请参阅@lundin的答案如何使用指针进入该池中的指针来避免严格的别名违规。
(如果您 do 碰巧知道必须确保对齐的所有类型,则将其中的每个类型都放入
Union Max_align
而不是d ,
或l
和p
,也可以通过将分配器发出指示为Union max_align
而不是指向<代码来使您的生活更轻松> void未签名的char
。)总体而言,您需要选择与绝对可移植性不同的目标。没有这样的东西。避免在C99中添加的编译器扩展和语言功能,后来是一个很好的开始。最大程度地减少您对实施行为的假设很重要。在不可行的地方,请选择您可以提出的最便携式选项,然后选择 文档 。
*更不用说您依赖于不在C90中的
uint8_t
,即使所有C99及以后的实现也不一定都提供。Unfortunately, this ...
... combined with this ...
... puts you absolutely up a creek, unless you know in advance all the structure types with which your memory pool may be used. Even the approach you are taking now does not conform strictly to C90, because there is no strictly-conforming way to determine the alignment of an address, so as to compute how much padding is needed.* (You have probably assumed that you can convert it to an integer and look at the least-significant bits, but C does not guarantee that you can determine anything about alignment that way.)
In practice, there is a variety of things that will work in a very wide range of target environments, despite not strictly conforming to the C language specification. Interpreting the result of converting a pointer to an integer as a machine address, so that it is sensible to use it for alignment computations, is one of those. For appropriately aligning a declared array, so would this be:
For a very large set of C implementations, that not only ensures that the pool itself is properly aligned for any use by strictly-conforming C90 clients, but it also conveniently divides the pool into aligned blocks on which your allocator can draw. Refer also to @Lundin's answer for how pointers into that pool would need to be used to avoid strict aliasing violations.
(If you do happen to know all the types for which you must ensure alignment, then put one of each of those into
union max_align
instead ofd
,l
, andp
, and also make your life easier by having the allocator hand out pointers tounion max_align
instead of pointers tovoid
orunsigned char
.)Overall, you need to choose a different objective than absolute portability. There is no such thing. Avoiding compiler extensions and language features added in C99 and later is a great start. Minimizing the assumptions you make about implementation behavior is important. And where that's not feasible, choose the most portable option you can come up with, and document it.
*Not to mention that you are relying on
uint8_t
, which is not in C90, and which is not necessarily provided even by all C99 and later implementations.