我刚刚开始使用另一个代码库,开发人员在复制/比较/设置时始终使用结构的第一个元素的地址,而不是结构本身。这是一个简单的例子。
首先是结构类型:
typedef struct {
int a;
int b;
} foo_t;
然后有一个函数可以复制这样的结构:
void bar(foo_t *inp)
{
foo_t l;
...
memcpy(&l.a, &inp->a, sizeof(foo_t));
...
}
我不会以这种方式给memcpy
写一个调用,我开始怀疑原始开发人员根本没有完全掌握C中的指针和结构。但是,现在我已经看到了这个在两个不相关的代码库中,没有共同的开发人员,所以我开始怀疑自己。
为什么要使用这种风格?
答案 0 :(得分:62)
没有人应该这样做。 如果重新排列struct成员,则会遇到麻烦。
答案 1 :(得分:62)
而不是:
memcpy(&l.a, &inp->a, sizeof(foo_t));
你可以这样做:
memcpy(&l, inp, sizeof(foo_t));
虽然它可能是危险的并且具有误导性,但是这两个语句实际上在这里做同样的事情,因为C保证在第一个结构成员之前没有填充。
但最好的方法是使用简单的赋值运算符复制结构对象:
l = *inp;
为什么要使用这种风格?
我的猜测:无知或严重的纪律。
答案 2 :(得分:16)
一个人不会。如果您曾在结构中移动a
或在其之前插入了成员,则会引入内存粉碎错误。
答案 3 :(得分:12)
此代码不安全,因为如果成员memcpy
不再是第一个成员,则重新排列结构的成员会导致a
访问超出结构的边界。
但是,可以想象成员是在结构中故意排序的,程序员只想复制它们的一个子集,开始与成员a
并运行直到结构的结尾。如果是这种情况,则可以通过以下更改使代码安全:
memcpy(&l.a, &inp->a, sizeof(foo_t) - offsetof(foo_t, a));
现在结构成员可以重新排列成任何顺序,而memcpy
永远不会超出界限。
答案 4 :(得分:1)
这是一个非常糟糕的习惯。例如,结构可能有另一个成员前置。这是一种疯狂粗心的习惯,我很惊讶地发现有人会这样做。
其他人已经注意到了这些;让我烦恼的是:
struct Foo rgFoo [3];
struct Foo *pfoo = &rgFoo [0];
而不是
struct Foo *pfoo = rgfoo;
为什么要通过索引对数组进行deref,然后再次获取地址?它已经是地址,唯一的区别在于pfoo在技术上是
struct Foo *const,
不是
struct Foo *.
然而,我曾经一直看到第一个。
答案 5 :(得分:1)
实际上,有一个合法的用例:构建一个类层次结构。
当将结构体作为类实例处理时,第一个成员(即偏移0)通常是超类型实例...如果存在超类型。这允许简单的强制转换在使用子类型与超类型之间移动。非常有用。
关于Darren Stone关于意图的说明,当用C语言执行OO时,这是预期。
在任何其他情况中,我建议避免使用此模式并直接访问该成员,原因已经引用。