memcpy vs C中的赋值 - 应该是memmove吗?

时间:2011-03-23 21:58:05

标签: c optimization gcc memcpy memmove

正如在an answer to this question中指出的那样,编译器(在这种情况下是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,那么问题就会消失。

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

3 个答案:

答案 0 :(得分:4)

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

顺便提一下,在您的示例中,o->i是无意义的。你的意思是o.i我认为......

答案 1 :(得分:4)

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

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

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

答案 2 :(得分:1)

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