为什么要使用struct的第一个元素的地址,而不是struct本身?

时间:2013-11-04 20:41:30

标签: c struct

我刚刚开始使用另一个代码库,开发人员在复制/比较/设置时始终使用结构的第一个元素的地址,而不是结构本身。这是一个简单的例子。

首先是结构类型:

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中的指针和结构。但是,现在我已经看到了这个在两个不相关的代码库中,没有共同的开发人员,所以我开始怀疑自己。

为什么要使用这种风格?

6 个答案:

答案 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时,这是预期

在任何其他情况中,我建议避免使用此模式并直接访问该成员,原因已经引用。