结构体成员 [数组 vs 指针]

3
声明 C 结构成员为大小为 1 的数组而不是指针有什么好处?
struct {
  a_struct_t a_member[1];
  ...
}b_struct;

Thanks in advance

5个回答

5

一般情况下,一个结构体中声明为数组的成员会被放置在结构体的最后一个位置。这是因为该结构体将要被动态地分配内存。当它被分配时,代码将为该数组中需要的元素数量分配空间:

struct X {
    time_t birthday;
    char name[1];
};

struct X *x = malloc(sizeof(*x) + 35);
x->birthday = mktime(&t);
strcpy(x->name, "no more than 35 characters");

这对于字符串特别有效--您在结构体中分配的字符为NUL终止符留出了空间,因此当您进行分配时,您分配的字符数恰好是要放置在那里的字符串的strlen()。对于大多数其他类型的项目,通常需要从分配大小中减去一(或仅接受分配的空间比严格必要的空间大一个项目)。

您可以使用指针(类似地)完成相同的操作,但它会导致将结构体的主体与通过指针引用的项目分别分配。好处是(与上述方法不同),可以动态分配多个项目,而上述方法仅适用于结构体的最后一个成员。


最新的C标准允许在结构体末尾使用char name[];,而gcc已经允许你在结构体末尾使用char name[0];相当长一段时间了,就是为了这个目的。 - nategoose

2

您所描述的是完全不同的两件事情。如果您有一个指针作为成员:

a_struct_t* a_member;

如果只是一个指针,那么结构体内部没有分配任何内存来保存 a_struct_t。另一方面,如果您有一个大小为1的数组:

a_struct_t a_member[1];

那么你的结构体实际上在其内部有一个类型为a_struct_t的对象。从内存角度来看,它与仅将该类型的对象放置在结构体内并没有多大区别:

a_struct_t a_member;

从使用角度来看,数组需要间接访问其一个元素(即,您需要使用*a_member而不是a_member)。


但正如布莱恩·W·柯林汉和丹尼斯·M·里奇在他们的书《C语言程序设计》中所说:“一个结构声明如果没有跟随变量列表,则不会保留任何存储空间;它仅仅描述了一个结构的模板或形状。”因此,在结构定义中,我对声明和内存分配存在困惑。 - Walidix
1
@Walidix:这允许您执行struct thing{ ...};,然后稍后说struct thing aThing, someThings[thingCount];。无论哪种方式,声明成员为数组或指针的struct thing的解释都不同。 - dmckee --- ex-moderator kitten

2

"大小为1的数组而不是指针"?抱歉,但我不明白这个问题怎么可能有意义。如果您问的是关于“大小为1的数组而不是普通成员(非数组)”,那我可以理解。但是“而不是指针”呢?指针与此有什么关系?它如何可替换数组,以证明这个问题?

如果您真正想问的是为什么它被声明为大小为1的数组而不是非数组,就像下面这样:

struct { 
  a_struct_t a_member; 
} b_struct; 

如果需要解释,那么其中一个可能的解释是被称为“结构体技巧”的众所周知的习语。你可能会看到这样的声明:

struct { 
  ...
  a_struct_t a_member[1]; 
} b_struct; 

实现可变大小数组的方法是将其作为结构对象的最后一个成员。实际的结构对象稍后在内存块中创建,该内存块足够大以容纳所需的任意数量的数组元素。但在这种情况下,数组必须是结构的最后一个成员,而不是您示例中的第一个成员。
附注:有时您可能会看到通过大小为0的数组实现“struct hack”,这实际上违反了C的约束(即编译错误)。

1
结构体黑科技通常在处理数据通信包时使用,其中“有效载荷”(或“数据包的其余部分”)自然出现在结构体的最后一个成员中。 - Kevin Little
只是举一个具体的例子,变长结构体通常被使用在哪里。一些真实世界的背景可以帮助解释“结构体黑科技”的好处(使用OP的话来说)。 - Kevin Little

0
这些是不同的东西。
这个成员的名称是已分配内存的地址,在结构实例本身中分配。

1
不,它不是“指向已分配内存的指针”。它本身就是已分配的内存。那里根本没有“指针”。 - AnT stands with Russia
但正如Brian W. Kernighan和Dennis M. Ritchie在他们的书《C编程语言》中所说: “如果结构声明后面没有跟着变量列表,那么它不会保留任何存储空间;它只是描述了一个结构的模板或形状。” 因此,在结构定义中,我对声明和内存分配存在困惑。您能否进一步解释一下。谢谢。 - Walidix

0

所以我认为已经说明了指针和数组之间的主要区别在于必须为指针分配内存。

你问题的棘手之处在于,即使你为结构体分配了空间,如果你的结构体包含一个指针,你还必须为指针再次分配内存,但指针本身将作为结构体分配的一部分而被分配。

如果你的结构体包含一个长度为1的数组,则无需分配任何额外的内存,它将存储在结构体中(你仍然需要为结构体分配内存)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接