为什么使用struct的第一个元素的地址,而不是结构本身?
在工作中,我刚刚遇到另一个代码库,开发人员在复制/比较/设置时始终使用结构的第一个元素的地址,而不是结构本身。 这是一个简单的例子。
首先有一个结构types:
typedef struct { int a; int b; } foo_t;
然后有一个函数可以创build这样一个结构体的副本:
void bar(foo_t *inp) { foo_t l; ... memcpy(&l.a, &inp->a, sizeof(foo_t)); ... }
我不会以这种方式给memcpy
写一个调用,我开始怀疑原来的开发人员根本不明白C中的指针和结构。然而,现在我已经看到了两个不相关的代码库,没有普通的开发者,所以我开始怀疑自己。
为什么要使用这种风格?
没有人应该这样做。 如果你重新排列结构成员,你有麻烦。
而不是:
memcpy(&l.a, &inp->a, sizeof(foo_t));
你可以这样做:
memcpy(&l, inp, sizeof(foo_t));
虽然这可能是危险的,误导性的,但是这两个陈述实际上在这里做着相同的事情,因为C保证在第一个结构成员之前没有填充。
但最好的方法是使用简单的赋值运算符来复制结构对象:
l = *inp;
为什么要使用这种风格?
我的猜测是:无知或不纪律。
一个不会。 如果你曾经在结构体中移动过a
,或者在它之前插入a
或多个成员,那么就会引入一个内存粉碎的bug。
这个代码是不安全的,因为如果成员a
不再是第a
成员,则重新排列结构体的成员可能会导致memcpy
访问结构的边界。
然而,可以想象的是,成员是有意在结构中sorting的,程序员只想复制它们的一个子集, 从成员a
开始直到结束。 如果是这种情况,那么可以通过以下更改来使代码安全:
memcpy(&l.a, &inp->a, sizeof(foo_t) - offsetof(foo_t, a));
现在结构成员可以重新排列成任何顺序,这个memcpy
永远不会超出界限。
这是一个非常坏的习惯。 例如,该结构可能会有另一个成员。 这是一个疯狂的粗心大意的习惯,我很惊讶地看到任何人都会这样做。
其他人已经注意到了这些; 误会我的是这样的:
struct Foo rgFoo [3]; struct Foo *pfoo = &rgFoo [0];
代替
struct Foo *pfoo = rgfoo;
为什么按索引对数组进行解列,然后重新input地址? 这已经是地址了,注意的唯一区别就是pfoo在技术上
struct Foo *const,
不
struct Foo *.
然而,我总是看到第一个。
实际上,有一个合法的用例:构build一个类层次结构。
将结构作为类实例进行处理时,第一个成员(即偏移量0)通常是超types实例…如果存在超types的话。 这允许简单的转换在使用子types和超types之间移动。 很有用。
关于Darren Stone关于意图的说明,在C语言中执行面向对象的时候会出现这种情况。
在任何其他情况下 ,我build议避免这种模式,直接访问该成员,原因已经提到。