在编译时在 const struct 类型之间转换
Converting between const struct types in compile time
给定一个 API 中的常量结构,在另一个 API 中将被解释为 16 个连续的 uint8_t 字节,C 中是否有一种方法可以在编译时间:
我想要实现的是
const union {
struct a {
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t d[8];
} a;
uint8_t b[16];
} foo = { .a = { 0x12341243, 0x9898, 0x4554,
{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 } } };
struct from_other_api manifest = {
.appuuid = foo.b;
// { foo.b[0], foo.b[1], ... }
};
不幸的是,这种方法以及注释行中的第二个版本都给出了错误 error: initializer element is not constant,尽管这肯定 看起来 就像一个常数。
业务原因是定义 struct from_other_api manifest
和常量内存 blob 都来自 API,不能修改。转换可以手动完成
struct from_other_api manifest = {
.appuuid = { 0x43, 0x12, 0x34, 0x12, 0x98, 0x98, 0x54, 0x45,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
};
但要避免,因为这是一种需要自动化的常规模式。
您不能在数组初始值设定项中传递文字以外的任何内容。
使用 memcpy(3)
代替:
struct from_other_api manifest = {
// initialize other members
};
memcpy(manifest.appuuid, foo.b, sizeof (manifest.appuuid));
这个声明没有声明变量。
struct a {
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t d[8];
};
要在 union
中声明一个名为 a
且具有相同结构的变量,请使用:
const union {
struct {
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t d[8];
} a; // now a is accessible with a.a, a.b, a.c and a.d[i].
uint8_t b[16];
} foo = { .a = { 0x12341243, 0x9898, 0x4554,
{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 } } };
在 C 中,常量变量不能用于常量表达式。
如果你可以在运行时初始化 manifest
,你可以用 memcpy
来完成,就像 Snaipes 的回答一样。
但是如果manifest
必须在编译时初始化,你可能需要(ab)使用预处理器。它不会太漂亮,但它有效:
#define ID 0x12341243, 0x9898, 0x4554, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
#define FOO(nnn) { .a = FOO2(nnn) }
#define FOO2(a, b, c, d, e, f, g, h, i, j, k) \
{ a, b, c, { d, e, f, g, h, i, j, k } }
#define MAN(nnn) MAN2(nnn)
#define MAN2(a, b, c, d, e, f, g, h, i, j, k) \
{ a >> 0 & 0xFF, a >> 8 & 0xFF, a >> 16 & 0xFF, a >> 24 & 0xFF, \
b >> 0 & 0xFF, b >> 8 & 0xFF, \
c >> 0 & 0xFF, c >> 8 & 0xFF, \
d, e, f, g, h, i, j, k }
const union {
...
} foo = FOO(ID);
struct from_other_api manifest = {
.appuuid = MAN(ID)
};
给定一个 API 中的常量结构,在另一个 API 中将被解释为 16 个连续的 uint8_t 字节,C 中是否有一种方法可以在编译时间:
我想要实现的是
const union {
struct a {
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t d[8];
} a;
uint8_t b[16];
} foo = { .a = { 0x12341243, 0x9898, 0x4554,
{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 } } };
struct from_other_api manifest = {
.appuuid = foo.b;
// { foo.b[0], foo.b[1], ... }
};
不幸的是,这种方法以及注释行中的第二个版本都给出了错误 error: initializer element is not constant,尽管这肯定 看起来 就像一个常数。
业务原因是定义 struct from_other_api manifest
和常量内存 blob 都来自 API,不能修改。转换可以手动完成
struct from_other_api manifest = {
.appuuid = { 0x43, 0x12, 0x34, 0x12, 0x98, 0x98, 0x54, 0x45,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
};
但要避免,因为这是一种需要自动化的常规模式。
您不能在数组初始值设定项中传递文字以外的任何内容。
使用 memcpy(3)
代替:
struct from_other_api manifest = {
// initialize other members
};
memcpy(manifest.appuuid, foo.b, sizeof (manifest.appuuid));
这个声明没有声明变量。
struct a {
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t d[8];
};
要在 union
中声明一个名为 a
且具有相同结构的变量,请使用:
const union {
struct {
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t d[8];
} a; // now a is accessible with a.a, a.b, a.c and a.d[i].
uint8_t b[16];
} foo = { .a = { 0x12341243, 0x9898, 0x4554,
{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 } } };
在 C 中,常量变量不能用于常量表达式。
如果你可以在运行时初始化 manifest
,你可以用 memcpy
来完成,就像 Snaipes 的回答一样。
但是如果manifest
必须在编译时初始化,你可能需要(ab)使用预处理器。它不会太漂亮,但它有效:
#define ID 0x12341243, 0x9898, 0x4554, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
#define FOO(nnn) { .a = FOO2(nnn) }
#define FOO2(a, b, c, d, e, f, g, h, i, j, k) \
{ a, b, c, { d, e, f, g, h, i, j, k } }
#define MAN(nnn) MAN2(nnn)
#define MAN2(a, b, c, d, e, f, g, h, i, j, k) \
{ a >> 0 & 0xFF, a >> 8 & 0xFF, a >> 16 & 0xFF, a >> 24 & 0xFF, \
b >> 0 & 0xFF, b >> 8 & 0xFF, \
c >> 0 & 0xFF, c >> 8 & 0xFF, \
d, e, f, g, h, i, j, k }
const union {
...
} foo = FOO(ID);
struct from_other_api manifest = {
.appuuid = MAN(ID)
};