假设我有这样的结构:
typedef struct {
char *str;
int len;
} INS;
该结构的数组。
INS *ins[N] = { &item, &item, ... }
当我尝试访问其元素时,不是作为指针,而是作为struct本身,所有字段都被复制到临时本地?
for (int i = 0; i < N; i++) {
INS in = *ins[i];
// internaly the above line would be like:
// in.str = ins[i]->str;
// in.len = ins[i]->len;
}
因此,当我增加结构字段时,这将是一个更昂贵的赋值操作?
答案 0 :(得分:2)
正确,in
是*ins[i]
的副本。
不要介意你的内存消耗,但你的代码很可能不正确:对象in
在循环体的末尾死亡,以及你对{{{ 1}}没有持久的效果!
答案 1 :(得分:1)
结构分配的行为类似于memcpy
。是的,对于更大的结构来说它更贵。矛盾的是,你的结构越大,衡量增加另一个领域的额外费用就越难。
答案 2 :(得分:1)
是的,struct在C中有value semantics
。因此,将结构分配给另一个将导致成员方式的副本。请记住,指针仍将指向相同的对象。
答案 3 :(得分:1)
编译器可能优化结构的副本,而是直接从数组访问成员以提供使用副本的C代码中所需的值,或者可以仅复制单个成员使用。一个好的编译器会做到这一点。
通过指针存储值可能会干扰此优化。例如,假设您的例程还有一个指向int p
的指针。当编译器处理您的代码INS in = *ins[i]
时,它可以“思考”类似这样的内容:“复制ins[i]
代价很高。相反,我会记得in
是一个副本,我会在以后使用它们时获取成员。“但是,如果您的代码包含*p = 3
,则可能会更改{{1}除非编译器能够推断出ins[i]
没有指向p
。 (有一种方法可以帮助编译器使用ins[i]
关键字进行扣除。)
总结:表面看起来很昂贵的操作可能会被一个好的编译器有效地实现。看起来便宜的操作可能很昂贵(写入restrict
打破了一个很大的优化)。通常,您应该编写清楚表达算法的代码并让编译器进行优化。
扩展编译器如何优化它。假设你写:
*p
其中“...”中的代码访问in.str和in.len,但不添加任何其他237个成员添加到INS结构中。然后,编译器可以自由地将此代码转换为:
for (int i = 0; i < N; i++) {
INS in = *ins[i];
...
}
即使你写了一个声明,表面上复制了所有INS结构,编译器只需要复制实际需要的部分。 (实际上,甚至不需要复制这些部分。只需要生成一个程序,该程序可以获得相同的结果就像它直接遵循源代码一样。)