为什么使用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议避免这种模式,直接访问该成员,原因已经提到。