使用C中的宏,可以从各种结构在预编译时构造任意字节数组吗?

使用C中的宏,可以从各种结构在预编译时构造任意字节数组吗?,c,arrays,struct,literals,C,Arrays,Struct,Literals,我们都知道,我们可以将C中的字符串文字“混搭”在一起,而不会让大多数编译器感到麻烦,例如char[]result=“a”“b”/result=“ab”。我想把这个想法推广到结构上 假设我有以下结构: typedef struct s1 { char a; int b; } s1_t; typedef struct s2 { int c; } s2_t; 实际上,我想声明字节数组,它们是s1\u t和s2\u t的任意组合。这是否合理可行?我宁愿不使用工会。结构的内容将在编译时已知 我们

我们都知道,我们可以将C中的字符串文字“混搭”在一起,而不会让大多数编译器感到麻烦,例如
char[]result=“a”“b”/result=“ab”
。我想把这个想法推广到结构上

假设我有以下结构:

typedef struct s1 {
 char a;
 int b;
} s1_t;

typedef struct s2 {
 int c;
} s2_t;
实际上,我想声明字节数组,它们是
s1\u t
s2\u t
的任意组合。这是否合理可行?我宁愿不使用工会。结构的内容将在编译时已知

我们都知道,我们可以在C语言中“混合”字符串文本,而不会让大多数编译器困扰我们

事实上,没有符合标准的C编译器会抱怨,因为这些构造的语义是由标准明确定义的。我提到这一点是为了强调,结果不是偶然的,取决于运气或编译器的突发奇想,或任何诸如此类的事情,正如您的评论似乎允许的可能性

实际上,我想声明的字节数组是[两种结构类型]的任意组合。这是否合理可行?我宁愿不使用工会

严格地说,这是不可能的。不能在任何组合中使用
struct
文本来初始化字节数组。使用联合可以在C99或更高版本中实现相当接近的功能,如果希望从
struct
literals进行编译时初始化,那么我看不到任何其他选项。它看起来是这样的:

typedef union {
    s1_t s1;
    s2_t s2;
} s_u;

s_u array[] = {
    { .s1 = (s1_t) { 'a', 42 } },
    { .s1 = (s1_t) { 'b', 17 } },
    { .s2 = (s2_t) { 1856 } },
    { .s1 = (s1_t) { 'Q', -1 } }
};

unsigned char *byte_array = (unsigned char *)array;
然而,我想,您想要避免联合的原因是将不同大小的
struct
表示打包在一起,而不使用填充。这是不可能的。实际上,即使只有一个结构类型,也不一定能够在一个实例的最后一个元素和下一个实例的第一个元素之间没有任何填充来排列实例。甚至用
memcpy()
强制它也不容易,因为
struct
s的表示形式既可以有尾随填充,也可以有内部填充,所有填充都按大小计算。不要忘记任何填充字节的值都是未定义的

您将所追求的结果描述为“顺序字节码”和“序列化结构”(重点添加)。我不能确定“序列化”在这个上下文中对您意味着什么,但对我来说,它通常意味着与“内部表示”完全不同的东西。事实上,避免依赖内部表示是序列化的主要原因之一


如果你的目标符合我的序列化思想,那么避免让你的用户编写字节码的最好办法就是为他们提供一个字节码编译器,以任何形式输入和输出都是最方便的。

也许可以通过一些令人讨厌的基于宏的陈词滥调来实现你想要的东西,如果您的数据结构是在一个单独的文件中定义的,那么当不同的宏生效时,哪些代码可以包含多次

例如,假设您的数据位于以下格式的文件
foo.dat

INTS(1,2)
IDBL(-4,3)
INTS(5,23)
项目应列在单独的行中,不带分号

我们可以从做以下事情开始:

#define INTS(x,y) INT_PAIR Field ## __LINE__;
#define IDBL(x,y) INT_DBL  Field ## __LINE__;
struct ALL_DATA {
#include "foo.dat"
  int END_OF_DATA;
};
#undef INTS
#undef INTS
然后是:

#define INTS(x,y) {(x),(y)},
#define IDBL(x,y) {(x),(y)},
const struct ALL_DATA all_data = {
#include "foo.dat"
  0};
#undef INTS
#undef INTS
此时,将有一个编译时常量结构,其中包含其他结构类型的组合,如果它是根据某种形式的已知规则构造的,则可以在运行时对其进行解析(例如,第一个值对于所有int/int对为正值,对于所有int/double对为负值,对于数据结尾为零)

如果想要有一个包含所有项目(以整数大小单位)从结构开始的起始偏移量的数组,可以使用更多包含项:

#define INTS(x,y) INT_PAIR FIELD_ID_ ## __LINE__, dummy1x ## __LINE__,
#define IDBL(x,y) INT_DBL  FIELD_ID_ ## __LINE__, \
    dummy1x ## __LINE__, dummy2x ## __LINE__,
enum DATA_IDS {
#include "foo.dat"
  dummy_end_id};
#undef INTS
#undef INTS
#define INTS(x,y) FIELD_ID_ ## __LINE__, 
#define IDBL(x,y) FIELD_ID_ ## __LINE__,
unsigned int data_offsets[] = {
#include "foo.dat"
  -1};
#undef INTS
#undef INTS

我不知道如何在不滥用
\uuuuu LINE\uuuu
指令或要求数据文件的每一行提供唯一的标识符名称的情况下实现这一点;哪种方法更可取可以公开讨论。

我可以问一下不使用工会的原因吗?在我看来,这似乎是一个使用它们的完美案例。老实说,如果你只是序列化到一个数组,在你想要的地方记住它,并称之为好的。更重要的是,因为您的大小不同,并且内存不会很好地对齐。您如何在源代码中键入
s1\u t
s2\u 2
文字?@JoachimPileborg嵌入式系统编程,每个字节都计数@MichaelOrganger
memcpy
要求我提前知道我将请求的字节数,或者我可以轻松地计算它,这在我的情况下是不正确的@ysap我们可以假设C99并使用复合文本(
(s1_t){a',10}
)或任何有效的东西。如果您无法为数据定义可预测的一致结构,那么您如何使用它?+1,但值得一提的是,有些编译器允许您在结构上强制执行“压缩”内存组织,从而消除了填充。例如,GCC有一个属性
packed
,用于标识该属性。