58

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

首先有一个结构类型:

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

为什么要使用这种风格?

4

6 回答 6

62

取而代之的是:

memcpy(&l.a, &inp->a, sizeof(foo_t));

你可以这样做:

memcpy(&l, inp, sizeof(foo_t));

虽然它可能是危险的和误导性的,但两个语句实际上在这里做同样的事情,因为 C 保证在第一个结构成员之前没有填充。

但最好的方法是使用简单的赋值运算符复制结构对象:

l = *inp;

为什么要使用这种风格?

我的猜测是:无知或不良纪律。

于 2013-11-04T20:47:48.640 回答
61

没有人应该这样做。如果你重新排列结构成员,你就有麻烦了。

于 2013-11-04T20:44:34.583 回答
16

一个不会。如果您曾经a在结构中移动或在其之前插入了成员,您将引入内存破坏错误。

于 2013-11-04T20:44:42.787 回答
12

此代码是不安全的,因为如果成员不再是第一个成员,则重新排列结构的成员可能会导致memcpy访问超出结构的边界。a

但是,可以想象成员在结构中是有意排序的,而程序员只想复制它们的一个子集,成员开始a并一直运行到结构的末尾。如果是这种情况,则可以通过以下更改使代码安全:

    memcpy(&l.a, &inp->a, sizeof(foo_t) - offsetof(foo_t, a));

现在结构成员可以重新排列成任何顺序,这memcpy永远不会超出界限。

于 2013-11-05T10:19:26.913 回答
2

实际上,有一个合理的用例:构建类层次结构。

当将结构视为类实例时,第一个成员(即偏移量 0)通常是超类型实例……如果存在超类型。这允许简单的转换在使用子类型和超类型之间移动。很有用。

在 Darren Stone 关于意图的注释中,这是在用 C 语言执行 OO 时预期的。

在任何其他情况下,出于已经引用的原因,我建议避免这种模式并直接访问该成员。

于 2015-05-28T19:22:26.227 回答
1

这真是个坏习惯。例如,该结构可能有另一个成员。这是一个非常粗心的习惯,我很惊讶有人会这样做。

其他人已经注意到了这些;困扰我的是:

struct Foo rgFoo [3];
struct Foo *pfoo = &rgFoo [0];

代替

struct Foo *pfoo = rgfoo;

为什么要按索引取消引用数组,然后再次获取地址?已经是地址了,唯一需要注意的区别是 pfoo 在技术上是

struct Foo *const, 

不是

struct Foo *.  

然而我以前总是看到第一个。

于 2013-11-06T16:15:10.480 回答