正如在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
,那么问题就会消失。
但这是一个编译器错误,还是那个赋值语句在某种程度上无效?
答案 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行为正确。