Я пытаюсь понять разницу между memcpy()
и memmove()
, и я прочитал текст, memcpy()
который не заботится о перекрывающихся источника и назначения, а memmove()
делает.
Однако когда я выполняю эти две функции на перекрывающихся блоках памяти, они оба дают одинаковый результат. Например, возьмите следующий пример MSDN на memmove()
странице справки:
Есть ли лучший пример, чтобы понять недостатки memcpy
и как memmove
это решить?
// crt_memcpy.c
// Illustrate overlapping copy: memmove always handles it correctly; memcpy may handle
// it correctly.
#include <memory.h>
#include <string.h>
#include <stdio.h>
char str1[7] = "aabbcc";
int main( void )
{
printf( "The string: %s\n", str1 );
memcpy( str1 + 2, str1, 4 );
printf( "New string: %s\n", str1 );
strcpy_s( str1, sizeof(str1), "aabbcc" ); // reset string
printf( "The string: %s\n", str1 );
memmove( str1 + 2, str1, 4 );
printf( "New string: %s\n", str1 );
}
Вывод:
The string: aabbcc
New string: aaaabb
The string: aabbcc
New string: aaaabb
memcpy
было быassert
то, что регионы не перекрываются, а не намеренно скрывают ошибки в вашем коде.The string: aabbcc New string: aaaaaa The string: aabbcc New string: aaaabb
Ответы:
Я не совсем удивлен, что в вашем примере нет странного поведения. Попробуйте скопировать
str1
вstr1+2
вместо этого и видеть то , что происходит потом. (Может не иметь никакого значения, зависит от компилятора / библиотек.)В общем, memcpy реализован простым (но быстрым) способом. Проще говоря, он просто перебирает данные (по порядку), копируя из одного места в другое. Это может привести к тому, что источник будет перезаписан во время чтения.
Memmove делает больше работы, чтобы обеспечить правильную обработку перекрытия.
РЕДАКТИРОВАТЬ:
(К сожалению, я не могу найти достойных примеров, но они подойдут). Сравните memcpy и memmove показанные здесь. memcpy просто зацикливается, а memmove выполняет тест, чтобы определить направление зацикливания, чтобы избежать повреждения данных. Эти реализации довольно просты. Большинство высокопроизводительных реализаций являются более сложными (включая копирование блоков размером в слово за раз, а не байтов).
источник
memmove
вызовыmemcpy
в одной ветке после тестирования указателей: student.cs.uwaterloo.ca/~cs350/common/os161-src-html/…memcpy
может быть быстрее.Память в
memcpy
не может перекрываться, или вы рискуете неопределенное поведение, в то время как памятьmemmove
может перекрываться.Некоторые реализации memcpy могут все еще работать для перекрывающихся входных данных, но вы не можете сосчитать это поведение. При этом memmove должен учитывать перекрытие.
источник
То, что
memcpy
не нужно иметь дело с перекрывающимися регионами, не означает, что они не справляются с ними правильно. Вызов с перекрывающимися областями вызывает неопределенное поведение. Неопределенное поведение может работать полностью так, как вы ожидаете на одной платформе; это не значит, что это правильно или правильно.источник
memcpy
она реализована точно так же, как иmemmove
. То есть тот, кто написал компилятор, не удосужился написать уникальнуюmemcpy
функцию.И memcpy, и memove делают схожие вещи.
Но чтобы заметить одно отличие:
дает:
источник
Ваша демоверсия не выявила недостатков memcpy из-за «плохого» компилятора, она делает вам одолжение в отладочной версии. Однако версия выпуска дает тот же результат, но из-за оптимизации.
Регистр
%eax
здесь играет роль временного хранилища, которое «элегантно» устраняет проблему перекрытия.Недостаток возникает при копировании 6 байтов, ну хотя бы его части.
Вывод:
Выглядит странно, это тоже связано с оптимизацией.
Вот почему я всегда выбираю
memmove
при попытке скопировать 2 перекрывающихся блока памяти.источник
Разница между
memcpy
иmemmove
заключается в том, чтов
memmove
, исходная память указанного размера копируется в буфер и затем перемещается в место назначения. Так что, если память перекрывается, побочных эффектов нет.в случае
memcpy()
, нет никакого дополнительного буфера, взятого для исходной памяти. Копирование выполняется непосредственно в память, поэтому при наличии совпадения памяти мы получаем неожиданные результаты.Это можно наблюдать с помощью следующего кода:
Выход:
источник
Как уже указывалось в других ответах,
memmove
он более сложный, чемmemcpy
тот, который учитывает перекрытия памяти. Результат memmove определяется как если бы онsrc
был скопирован в буфер, а затем скопирован в буферdst
. Это НЕ означает, что фактическая реализация использует какой-либо буфер, но, вероятно, выполняет некоторую арифметику указателей.источник
Компилятор может оптимизировать memcpy, например:
Этот memcpy может быть оптимизирован как:
x = *(int*)some_pointer;
источник
int
доступ. На некоторых архитектурах (например, Cortex-M0) попытка получить 32-битныйint
адрес, который не кратен четырем, вызовет сбой (ноmemcpy
будет работать). Если кто-либо будет использовать ЦП, который разрешает не выровненный доступ, или использовать компилятор с ключевым словом, которое указывает компилятору собирать целые числа из отдельно выбранных байтов, когда это необходимо, можно сделать что-то подобное,#define UNALIGNED __unaligned
а затем `x = * (int UNALIGNED * ) some_pointer;char x = "12345"; int *i; i = *(int *)(x + 1);
но некоторые делают, потому что они исправляют копию во время сбоя. Я работал над такой системой, и потребовалось немного времени, чтобы понять, почему производительность такая низкая.*(int *)some_pointer
это строгое нарушение псевдонимов, но вы, вероятно, имеете в виду, что компилятор будет выводить сборку, которая копирует intКод, приведенный в ссылках http://clc-wiki.net/wiki/memcpy для memcpy, кажется, немного смущает меня, поскольку он не дает того же результата, когда я реализовал его, используя приведенный ниже пример.
Вывод :
Но теперь вы можете понять, почему memmove позаботится о перекрывающихся проблемах.
источник
С11 стандартная тяга
Проект стандарта C11 N1570 гласит:
7.24.2.1 «Функция memcpy»:
7.24.2.2 «Функция memmove»:
Следовательно, любое совпадение
memcpy
приводит к неопределенному поведению, и может произойти все что угодно: плохое, ничего или даже хорошее. Хорошо это редко, хотя :-)memmove
однако ясно говорит, что все происходит так, как будто используется промежуточный буфер, поэтому ясно, что перекрытия в порядке.Однако C ++
std::copy
более простителен и допускает перекрытия: обрабатывает ли std :: copy перекрывающиеся диапазоны?источник
memmove
использовать дополнительный временный массив n, поэтому он использует дополнительную память? Но как это возможно, если мы не дали ему доступ к какой-либо памяти. (Используется в 2 раза больше памяти).Я попытался запустить ту же программу, используя Eclipse, и она показывает четкую разницу между
memcpy
иmemmove
.memcpy()
не заботится о перекрытии области памяти, которая приводит к повреждению данных, аmemmove()
сначала скопирует данные во временную переменную, а затем скопирует в реальную область памяти.При попытке скопировать данные из местоположения
str1
вstr1+2
, выводmemcpy
"aaaaaa
". Вопрос будет как?memcpy()
будет копировать один байт за раз слева направо. Как показано в вашей программе "aabbcc
", тогда все копирование будет осуществляться, как показано ниже,aabbcc -> aaabcc
aaabcc -> aaaacc
aaaacc -> aaaaac
aaaaac -> aaaaaa
memmove()
сначала скопирует данные во временную переменную, а затем скопирует в фактическую ячейку памяти.aabbcc(actual) -> aabbcc(temp)
aabbcc(temp) -> aaabcc(act)
aabbcc(temp) -> aaaacc(act)
aabbcc(temp) -> aaaabc(act)
aabbcc(temp) -> aaaabb(act)
Выход
memcpy
:aaaaaa
memmove
:aaaabb
источник
memmove()
копирует в промежуточное местоположение. Он должен просто копировать в обратном порядке, когда это необходимо.