C语言标准不允许您指定struct
英寸S的内存布局,直到最后一位.其他语言可以(我会想到Ada和Erlang),但C语言不会.
因此,如果您想要实际的可移植标准C,您可以为您的数据指定一个Cstruct
,并使用指针将其转换为特定的内存布局,可能是由许多uint8_t
值组成并分解成许多值,以避免字符顺序问题.编写这样的代码很容易出错,需要重复内存,并且根据您的用例,它在内存和处理方面可能都相对昂贵.
如果您想通过C中的struct
直接访问内存布局,则需要依赖C语言规范中没有的编译器功能,因此不是"可移植的C语言".
因此,下一个最好的办法是使C代码尽可能地可移植:您可以定义struct
,并 for each 支持的平台和编译器组合提供特定于平台/编译器的代码,并且使用struct
的代码在每个平台/编译器上可以是相同的.
现在,您需要确保在内存布局与代码和外部接口所需的不完全相同的情况下,不会意外地为平台/编译器进行编译.
从C11开始,使用static_assert
、sizeof
和offsetof
就可以做到这一点.
因此,如果您可以要求C11(我假设您可以要求C11,因为您使用的是alignas
,它不是C99的一部分,而是C11的一部分),下面的内容应该可以完成这项工作.这里的"可移植C"部分是修复每个平台/编译器的代码,在这些平台/编译器中,由于static_assert
个声明中的一个声明失败而导致编译失败.
#include <assert.h>
#include <stdalign.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
typedef uint16_t Node8[8];
typedef struct Octree {
uint64_t *data;
uint8_t alignas(8) alloc;
uint8_t dataalloc;
uint16_t size, datasize, node0;
Node8 alignas(16) node[];
} Octree;
static_assert(0x10 == sizeof(Octree), "Octree size error");
static_assert(0x00 == offsetof(Octree, data), "Octree data position error");
static_assert(0x08 == offsetof(Octree, alloc), "Octree alloc position error");
static_assert(0x09 == offsetof(Octree, dataalloc), "Octree dataalloc position error");
static_assert(0x0a == offsetof(Octree, size), "Octree size position error");
static_assert(0x0c == offsetof(Octree, datasize), "Octree datasize position error");
static_assert(0x0e == offsetof(Octree, node0), "Octree node0 position error");
static_assert(0x10 == offsetof(Octree, node), "Octree node[] position error");
使用预处理器宏串行化struct
名称、成员名称以及可能的大小/偏移量值,可以更简洁地编写包含static_assert
个声明的系列,减少错误消息的冗余源代码输入.
正如问题的注释中所指出的,您将需要单独验证未定义的行为&(((uint16_t *)octree.node)[-1]) == &octree.node0
实际上是您在此编译器/平台上期望的行为.理想情况下,您会找到一种方法将其作为单独的static_assert
声明来编写.但是,像测试这样的测试足够快,以至于您可以将这样的判断添加到运行时代码中的一个很少但保证会运行的函数中,比如全局初始化函数.