10

正如对这个问题的回答所指出的那样,编译器(在这种情况下是 gcc-4.1.2,是的,它很旧,不,我不能改变它)可以在它认为合适的地方用 memcpy 替换结构分配。

我在 valgrind 下运行一些代码,并收到有关 memcpy 源/目标重叠的警告。当我查看代码时,我看到了这个(释义):

struct outer
{
    struct inner i;
    // lots of other stuff
};

struct inner
{
    int x;
    // lots of other stuff
};

void frob(struct inner* i, struct outer* o)
{
    o->i = *i;
}

int main()
{
    struct outer o;

    // assign a bunch of fields in o->i...

    frob(&o.i, o);
    return 0;
}

如果 gcc 决定用 替换该分配memcpy,那么这是一个无效的调用,因为源和目标重叠。

显然,如果我将赋值语句frob改为调用memmove,那么问题就消失了。

但这是一个编译器错误,还是该赋值语句在某种程度上无效?

4

3 回答 3

4

据我所知,这是一个编译器错误。i允许&o.i根据别名规则进行别名,因为类型匹配并且编译器无法证明o.i之前不能获取的地址。当然memcpy,使用重叠(或相同)指针调用会调用 UB。

顺便说一句,在您的示例中,o->i这是无稽之谈。你的意思是o.i我认为...

于 2011-03-23T22:06:36.330 回答
4

我认为你混淆了水平。gcc通过调用任何它喜欢的库函数来替换赋值操作是完全正确的,只要它可以保证正确的行为。

它不是memcpy标准意义上的“呼叫”或任何东西。它只是使用它的库中的一个函数,它可能有额外的信息来保证正确性。标准中描述的属性memcpy是被视为程序员接口的属性,而不是编译器/环境实现者的属性。

在该实现中是否memcpy实现了使其对赋值操作有效的行为是另一个问题。检查甚至检查代码应该不难。

于 2011-03-23T22:34:22.707 回答
1

我想有一个错字:“&o”而不是“0”。在这个假设下,“重叠”实际上是一个严格的覆盖:memcpy(&o->i,&o->i,sizeof(o->i))。在这种特殊情况下,memcpy 行为正确。

于 2011-03-23T22:07:48.673 回答