value 不是数组,它是一个指针(指向结构之外的某个位置)。如果您想要数组(大小未知),请改写unsigned char value[1]。
typedef struct __attribute__((packed)) {
unsigned char type;
unsigned char length;
unsigned char value[1];
} TLV;
拥有一个大小为 1 的数组允许您实际寻址任意数量的字节。这实际上是 UB,但它实际上在我看到的所有情况下都被使用并正常工作。
GCC 允许使用大小为 0 的数组。我已经习惯了这个约定,以至于我忘记了 C 中不允许大小为 0 的数组。
编辑:
长答案
数组和指针是有区别的。虽然您可以使用相似的代码来处理两者,但它们仍然是不同的野兽。
免责声明:以下代码在 gcc 中有效,但可能并非严格有效。我没有试图让它完全有效。
让我们定义两个结构:
typedef struct {
char p[20];
} sa;
typedef struct {
char *p;
} sp;
并创建这些实例:
sa x = { "Hello, world" };
sp y = { "Howdy, world" };
这两者有什么区别?
printf("%s\n", x.p); // prints "Hello, world"
printf("%s\n", y.p); // prints "Howdy, world"
这些地址呢?
printf("address of x = %p\n", &x); // On my machine it prints 0x7fffacce9b20
printf("address of y = %p\n", &y); // 0x7fffacce9b10
嗯.. 不是很有趣,只是这些数字.. 非常相似 - 两个结构都位于大约相同的位置 - 在我的例子中,它是堆栈,地址空间的结尾,但可能在其他地方。
printf("address of x.p = %p\n", &x.p); // 0x7fffacce9b20
printf("address of y.p = %p\n", &y.p); // 0x7fffacce9b10
相同的数字。果然不出所料。
printf("address of x.p[0] = %p\n", &x.p[0]); // 0x7fffacce9b20 - same as before
printf("address of y.p[0] = %p\n", &y.p[0]); // 0x400764 - could be anything
现在这些不同了。字符串 "Hello, world" 与结构 x 位于同一位置,而字符串 "Howdy, world" 位于其他位置 - 数据段,位于地址空间开头的某个位置,但也可能位于其他位置。
所以这就是区别:数组是存储在“这里”的一些数据,而指针只是存储在“某处”的数据的地址。
在您的情况下,您希望能够将数据保存在“此处”的某个位置 - 就在类型和长度之后。这就是为什么你需要数组,而不是指针。
我找不到任何证据证明上面的 TLV 实现不是 UB,但我看到很多情况下,通过将字符数组转换为指向某个结构的指针来“解析”字符数组。我什至自己也写过这样的代码。
0 大小数组
正如我之前所说,C 标准不允许大小为 0 的数组。但它们是 GCC 允许的,这很方便,因为它允许您执行以下操作:
typedef struct {
unsigned char type;
unsigned char length;
unsigned char value[0];
} TLV;
int required_length = 10;
TLV *tlv = (TLV *) malloc(sizeof(TLV) + required_length);
如果没有 0 大小的数组,您必须在上面的代码中的某处添加(或减去?我猜是减去)1。