Jęd*_*icz 7 c portability types alignment
我正在为一个非常简单的语言编写一个非常小的解释器,它允许简单的结构定义(由其他结构和简单类型组成,如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);
}
Run Code Online (Sandbox Code Playgroud)
这有效,但也许有更好的东西?
在C11中,它增加了_Alignof:
printf("Alignment of int: %zu\n", _Alignof(int));
Run Code Online (Sandbox Code Playgroud)
包含通常更好的样式<stdalign.h>,并使用小写alignof:
#include <stdalign.h>
printf("Alignment of int: %zu\n", alignof(int));
Run Code Online (Sandbox Code Playgroud)
你可以这样检查C11:
#if __STDC_VERSION__ >= 201112L
/* C11 */
#else
/* not C11 */
#endif
Run Code Online (Sandbox Code Playgroud)
如果您正在使用GCC或CLang,则可以通过添加-std=c11(或者-std=gnu11如果您还需要GNU扩展)在C11模式下编译代码.默认模式适用gnu89于GCC和gnu99CLang.
如果您进行一些有根据的猜测,您可能根本不需要检查系统的对齐情况.我建议使用以下两种顺序之一:
// 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
Run Code Online (Sandbox Code Playgroud)
这种排序是完全可移植的(但并不总是最佳的),因为最坏的情况只是你获得了更多的填充.
接下来是一个(公认的冗长)理由.如果你不关心我如何得出这个订单的结论,请随意跳过这一点.
这在C中都适用(无论实现如何):
// for both `signed` and `unsigned`
sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)
sizeof(float) <= sizeof(double) <= sizeof(long double)
Run Code Online (Sandbox Code Playgroud)
稍微修改一下订单,在大多数情况下你应该可以获得无衬垫结构.请注意,这并不能保证结构不会打开; 但它应该是大多数真实世界的情况,应该是GoodEnough™.
以下是特定于实现的建议,但在大多数情况下应涵盖一致性.
然而,这在任何地方都是完全可移植的(即使不是最佳的) - 只要你接受可能有一些填充(换句话说,不要假设结构没有任何填充).如果你弄错了,你所得到的只是一个更大的结构,所以不存在任何类型的未定义行为的危险.
您应该做的是从最大到最小订购这些,因为它们的对齐也将按此顺序排列.假设一个典型的amd64编译器:
long long a; // 8-byte
long b; // 8-byte or 4-byte; already aligned in both cases
int c; // 4-byte; already aligned
short d, e; // 2-byte; both already aligned
char f; // 1-byte; always aligned
Run Code Online (Sandbox Code Playgroud)
所以让我们从整数类型开始搞清楚我们的顺序:
long long, long, int, short, char
Run Code Online (Sandbox Code Playgroud)
现在,浮点类型.你做什么的double?它的对齐在64位体系结构上通常为 8个字节,在32位上为4个字节(但在某些情况下可能为8个字节).
long long始终至少为8字节(由于其最小范围,这是标准所要求的),并且long始终至少为4字节(但通常在64位中为 8字节;存在例如Windows的异常).
我会做的是放在double那些之间.注意,double大小可以是4个字节(通常在嵌入式系统中,例如AVR/Arduino),但实际上总是有4个字节long.
long double是一个复杂的案例.它的对齐范围可以从4字节(例如,x86 Linux)到16字节(amd64 Linux).然而,4字节对齐是一个历史人工制品并且不是最理想的; 所以我认为它至少是8字节,并把它放在上面long long.当它的对齐为16字节时,这也将使其最佳.
这留下了float,实际上总是一个4字节的数量,具有4字节对齐; 我将它介于两者之间long,保证至少为4字节,并且int可以(通常)为4或2字节.
所有这些结合起来给我们下一个订单:
long double, long long, double, long, float, int, short, char
Run Code Online (Sandbox Code Playgroud)
我们现在剩下的都是指针类型.不同的非函数指针的大小不一定相同,但我会假设它是(并且在绝大多数情况下,如果不是全部的情况下都是如此).我假设函数指针可以更大(认为ROM比RAM大的硬件架构),所以我会把它们放在其他的上面.
最糟糕的实际情况是它们是相同的,所以我什么都没有做到; 最好的情况是我已经消除了一些填充.
但是尺寸呢?这通常适用于非嵌入式系统:
sizeof(long) <= sizeof(T*) <= sizeof(long long)
Run Code Online (Sandbox Code Playgroud)
在大多数系统中,sizeof(long)并且sizeof(T*)是相同的; 但是例如64位Windows有32位long和64位T*.但是,在嵌入式系统中,它是不同的; 指针可以是16位,这意味着:
sizeof(int) <= sizeof(T*) <= sizeof(long)
Run Code Online (Sandbox Code Playgroud)
在这做什么取决于你--- 你是谁知道这通常会在哪里运行.一方面,针对主要用途非嵌入的嵌入式进行优化,意味着针对不常见的情况进行优化.另一方面,嵌入式系统中的内存比例更受限制.就个人而言,我建议优化桌面使用,除非您专门制作嵌入式应用程序.由于对齐double通常与指针大小相同但可以更大,我将其放在下面double.
// non-embedded
long double, long long, void (*)(void), void*, double, long, float, int, short, char
Run Code Online (Sandbox Code Playgroud)
对于嵌入式用途,我把它放在下面float,因为对齐float通常是4字节,但是T*是2字节或4字节:
// embedded
long double, long long, double, long, float, void (*)(void), void*, int, short, char
Run Code Online (Sandbox Code Playgroud)
这可能不是很便携,但GCC 接受以下内容:
#define alignof(type) offsetof(struct { char c; type d; }, d)
Run Code Online (Sandbox Code Playgroud)
编辑:根据这个答案,C 允许转换为匿名结构类型(尽管我希望看到这个语句得到备份)。所以以下内容应该是可移植的:
#define alignof(type) ((size_t)&((struct { char c; type d; } *)0)->d)
Run Code Online (Sandbox Code Playgroud)
另一种使用GNU 语句表达式的方法:
#define alignof(type) ({ \
struct s { char c; type d; }; \
offsetof(struct s, d); \
})
Run Code Online (Sandbox Code Playgroud)