C-可移植地获取类型对齐

C-可移植地获取类型对齐,c,types,alignment,portability,C,Types,Alignment,Portability,我正在为一种非常简单的语言编写一个非常小的解释器,它允许简单的结构定义(由其他结构和简单类型组成,比如int、char、float、double等等)。我希望字段使用尽可能少的对齐方式,因此使用max_align_t或类似的方式是不可能的。现在,我想知道除此之外,是否有更好的方法来获得任何单一类型的对齐: #include <stdio.h> #include <stddef.h> #define GA(type, name) struct GA_##name { ch

我正在为一种非常简单的语言编写一个非常小的解释器,它允许简单的结构定义(由其他结构和简单类型组成,比如int、char、float、double等等)。我希望字段使用尽可能少的对齐方式,因此使用max_align_t或类似的方式是不可能的。现在,我想知道除此之外,是否有更好的方法来获得任何单一类型的对齐:

#include <stdio.h>
#include <stddef.h>

#define GA(type, name) struct GA_##name { char c; type d; }; \
    const unsigned int alignment_for_##name = offsetof(struct GA_##name, d);

GA(int, int);
GA(short, short);
GA(char, char);
GA(float, float);
GA(double, double);
GA(char*, char_ptr);
GA(void*, void_ptr);

#define GP(type, name) printf("alignment of "#name" is: %dn", alignment_for_##name);

int main() {
GP(int, int);
GP(short, short);
GP(char, char);
GP(float, float);
GP(double, double);
GP(char*, char_ptr);
GP(void*, void_ptr);
}
#包括
#包括
#定义GA(类型,名称)结构GA##名称{char c;类型d;}\
const unsigned int alignment_for_35;##name=offsetof(struct GA####name,d);
GA(int,int);
GA(短,短);
GA(char,char);
GA(浮动,浮动);
GA(双,双);
GA(char*,char_ptr);
GA(无效*,无效ptr);
#定义GP(类型、名称)printf(“名称的对齐方式为:%dn”,名称的对齐方式为“;
int main(){
GP(int,int);
GP(短,短);
GP(char,char);
GP(浮动,浮动);
GP(双,双);
GP(字符*,字符ptr);
GP(无效*,无效ptr);
}

这是可行的,但也许还有更好的东西?

在C11中,它添加了
\u Alignof

printf("Alignment of int: %zu\n", _Alignof(int));
#include <stdalign.h>

printf("Alignment of int: %zu\n", alignof(int));
通常更好的样式是包含
,并使用小写的
alignof

printf("Alignment of int: %zu\n", _Alignof(int));
#include <stdalign.h>

printf("Alignment of int: %zu\n", alignof(int));
如果您使用的是GCC或CLang,您可以通过添加
-std=C11
(或者
-std=gnu11
(如果您还需要GNU扩展),在C11模式下编译代码。对于GCC,默认模式为
gnu89
,对于CLang,默认模式为
gnu99


更新: 如果进行一些有根据的猜测,您可能根本不需要检查系统的对齐情况。我建议使用以下两种订购方式之一:

// non-embedded use
long double, long long, void (*)(void), void*, double, long, float, int, short, char

// embedded use (microcontrollers)
long double, long long, double, long, float, void (*)(void), void*, int, short, char
这种排序是完全可移植的(但并不总是最优的),因为最坏的情况是你得到了比其他情况更多的填充

下面是一个(公认冗长的)理由。如果您不在乎我是如何得出订购结论的,请跳过这一点。


涵盖大多数情况 这在C中适用(无论实现如何):

整数类型 让我们开始计算顺序,从整数类型开始:

long long, long, int, short, char
浮点类型 现在,浮点类型。您如何处理双倍密码?它的对齐方式在64位体系结构上通常是8字节,在32位上通常是4字节(但在某些情况下可以是8字节)

long-long
始终至少为8字节(这是标准的隐含要求,因为它的最小范围),并且
long
始终至少为4字节(但通常为64位的8字节;也有例外,如Windows)

我要做的是把
double
放在它们之间。请注意,
double
的大小可以是4字节(通常在嵌入式系统中,如AVR/Arduino),但实际上这些系统的长度总是4字节

long double
是一个复杂的情况。它的对齐范围可以从4字节(比如x86Linux)到16字节(amd64 Linux)。然而,4字节对齐是一个历史产物,是次优的;所以我假设它至少是8字节,并将它放在
long
上面。当对齐为16字节时,这也将使其达到最佳状态

这就留下了
浮动
,实际上总是一个4字节的数量,4字节对齐;我将把它放在
long
int
之间,前者保证至少是4字节,后者(通常)可以是4或2字节

所有这些结合起来,我们下一个订单:

long double, long long, double, long, float, int, short, char
指针类型 我们现在只剩下指针类型了。不同的非函数指针的大小不一定相同,但我将假设它是相同的(在绝大多数情况下,如果不是所有情况下,也是如此)。我假设函数指针可以更大(想想ROM比RAM大的硬件架构),所以我会把它们放在其他指针之上

最糟糕的实际情况是,它们都是一样的,所以我什么也没做;最好的情况是我去掉了一些填充物

但是尺寸呢?这通常适用于非嵌入式系统:

在这里做什么取决于你——你是知道这通常会发生在哪里的人。一方面,针对主要用途为非的嵌入式应用程序进行优化意味着针对不常见情况进行优化。另一方面,在嵌入式系统中,内存受到的限制更大。就个人而言,我建议针对桌面使用进行优化,除非您专门制作嵌入式应用程序。由于
double
的对齐方式通常与指针大小相同,但可以更大,因此我将其放在
double
下面

// non-embedded
long double, long long, void (*)(void), void*, double, long, float, int, short, char
对于嵌入式应用,我把它放在
float
下面,因为
float
的对齐通常是4字节,但
T*
是2字节或4字节:

// embedded
long double, long long, double, long, float, void (*)(void), void*, int, short, char
这可能不是很容易移植,但GCC接受以下内容:

#define alignof(type) offsetof(struct { char c; type d; }, d)
编辑:根据,C允许对匿名结构类型进行强制转换(尽管我希望看到此语句得到备份)。因此,以下设备应便于携带:

#define alignof(type) ((size_t)&((struct { char c; type d; } *)0)->d)
另一种方法是:


我很乐意使用C11特性,不幸的是,由于一些业务/技术限制,我不得不留在C89/C90领域(是的,旧机器上的旧编译器)。不管怎样,我给+1,因为一旦这些镣铐被打破,使用它会很好。@JędrzejDudkiewicz:我现在已经大大扩展了我的答案,使用了一个通常有效的顺序。不过,大部分更新都只是基本原理,所以你可以看到我的理由。非常感谢大家的讨论。老实说,我不太确定该怎么处理它,因为我特别问了如何获得对齐,而最佳答案是由nwellnhof提供的。与此同时,你所写的内容很有用,而且肯定很有见地——我能不能给你的答案赋予不止一个“点”,或者没有办法做到这一点?@JędrzejDudkiewicz:我认为没有办法做到这一点,但别担心。无论如何谢谢你!谢谢,看来最好的解决方案是:#定义alignof(type)(size_t)(offsetof(struct{
// embedded
long double, long long, double, long, float, void (*)(void), void*, int, short, char
#define alignof(type) offsetof(struct { char c; type d; }, d)
#define alignof(type) ((size_t)&((struct { char c; type d; } *)0)->d)
#define alignof(type) ({ \
    struct s { char c; type d; }; \
    offsetof(struct s, d); \
})