memcpy против назначения в C - должно быть memmove? - PullRequest
9 голосов
/ 24 марта 2011

Как указано в ответе на этот вопрос , компилятор (в данном случае 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, то это недопустимый вызов, потому что source и dest перекрываются.

Очевидно, что если я заменю оператор присваивания в frob на вызов memmove, то проблема исчезнет.

Но это ошибка компилятора или этот оператор присваивания каким-то образом недопустим?

Ответы [ 3 ]

4 голосов
/ 24 марта 2011

Я думаю, что вы путаете уровни. gcc совершенно правильно заменить операцию присваивания вызовом любой библиотечной функции по своему усмотрению, если она может гарантировать правильное поведение.

Это не «призвание» memcpy или что-либо еще в смысле стандарта. Он просто использует одну функцию в своей библиотеке, для которой может иметь дополнительную информацию, гарантирующую правильность. Свойства memcpy, как они описаны в стандарте, являются свойствами, которые рассматриваются как интерфейсы для программиста, а не для компилятора / разработчика среды.

Реализовано ли в этой рассматриваемой реализации поведение memcpy, которое делает его действительным для операции присваивания, - это другой вопрос. Не должно быть так сложно проверить это или даже проверить код.

4 голосов
/ 24 марта 2011

Насколько я могу судить, это ошибка компилятора.i допускается для псевдонима &o.i в соответствии с правилами псевдонимов, поскольку типы совпадают, и компилятор не может доказать, что адрес o.i не мог быть ранее получен.И, конечно, вызов memcpy с перекрывающимися (или одинаковыми) указателями вызывает UB.

Кстати, обратите внимание, что в вашем примере o->i - это нонсенс.Вы имели в виду o.i Я думаю ...

1 голос
/ 24 марта 2011

Я предполагаю, что есть опечатка: "& o" вместо "0". Согласно этой гипотезе, «перекрытие» на самом деле является строгим перезаписью: memcpy (& o-> i, & o-> i, sizeof (o-> i)). В этом конкретном случае memcpy ведет себя корректно.

...