memcpy () vs memmove()
Я пытаюсь понять разницу между 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: %sn", str1 );
memcpy( str1 + 2, str1, 4 );
printf( "New string: %sn", str1 );
strcpy_s( str1, sizeof(str1), "aabbcc" ); // reset string
printf( "The string: %sn", str1 );
memmove( str1 + 2, str1, 4 );
printf( "New string: %sn", str1 );
}
выход:
The string: aabbcc
New string: aaaabb
The string: aabbcc
New string: aaaabb
10 ответов:
Я не совсем удивлен, что ваш пример не демонстрирует странного поведения. Попробуйте скопировать
str1
доstr1+2
вместо этого и посмотрим, что получится. (На самом деле это может не иметь значения, зависит от компилятора/библиотек.)В общем случае memcpy реализуется простым (но быстрым) способом. Упрощенно, он просто перебирает данные (по порядку), копирование из одного места в другое. Это может привести к перезаписи источника во время его чтения.
Memmove делает больше работы, чтобы убедиться, что он правильно обрабатывает перекрытия.
EDIT:
(к сожалению, я не могу найти достойные примеры, но они будут делать). Контраст memcpy и memmove реализации, показанный здесь. memcpy просто зацикливается, в то время как memmove выполняет тест, чтобы определить, в каком направлении зацикливаться, чтобы избежать повреждения данных. Эти реализации довольно просты. Большинство высокопроизводительных реализаций являются более сложными (включая копирование блоки размера слова за один раз, а не байты).
память
memcpy
не может перекрытие или вы рискуете неопределенным поведением, в то время как память вmemmove
могут пересекаться.char a[16]; char b[16]; memcpy(a,b,16); // valid memmove(a,b,16); // Also valid, but slower than memcpy. memcpy(&a[0], &a[1],10); // Not valid since it overlaps. memmove(&a[0], &a[1],10); // valid.
некоторые реализации memcpy могут по-прежнему работать для перекрывающихся входов, но вы не можете рассчитывать на такое поведение. В то время как memmove должен учитывать перекрытие.
просто так
memcpy
не нужно иметь дело с перекрывающимися регионами, не означает, что он не справляется с ними правильно. Вызов с перекрывающимися областями приводит к неопределенному поведению. Неопределенное поведение может работать полностью так, как вы ожидаете на одной платформе; это не означает, что это правильно или допустимо.
и memcpy и memove делают похожие вещи.
но чтобы увидеть одно отличие:
#include <memory.h> #include <string.h> #include <stdio.h> char str1[17] = "abcdef"; int main() { printf( "The string: %s\n", str1 ); memcpy( (str1+6), str1, 10 ); printf( "New string: %s\n", str1 ); strcpy_s( str1, sizeof(str1), "aabbcc" ); // reset string printf( "The string: %s\n", str1 ); memmove( (str1+6), str1, 10 ); printf( "New string: %s\n", str1 ); }
выдает:
The string: abcdef New string: abcdefabcdefabcd The string: abcdef New string: abcdefabcdef
ваша демонстрация не выявила недостатков memcpy из-за" плохого " компилятора, это делает вам одолжение в отладочной версии. Версия выпуска, однако, дает вам тот же результат, но из-за оптимизации.
memcpy(str1 + 2, str1, 4); 00241013 mov eax,dword ptr [str1 (243018h)] // load 4 bytes from source string printf("New string: %s\n", str1); 00241018 push offset str1 (243018h) 0024101D push offset string "New string: %s\n" (242104h) 00241022 mov dword ptr [str1+2 (24301Ah)],eax // put 4 bytes to destination 00241027 call esi
регистр
%eax
здесь играет как временное хранилище, которое "элегантно" исправляет проблему перекрытия.недостаток возникает при копировании 6 байт, ну хотя бы часть из них.
char str1[9] = "aabbccdd"; int main( void ) { printf("The string: %s\n", str1); memcpy(str1 + 2, str1, 6); printf("New string: %s\n", str1); strcpy_s(str1, sizeof(str1), "aabbccdd"); // reset string printf("The string: %s\n", str1); memmove(str1 + 2, str1, 6); printf("New string: %s\n", str1); }
выход:
The string: aabbccdd New string: aaaabbbb The string: aabbccdd New string: aaaabbcc
выглядит странно, это вызвало по оптимизации тоже.
memcpy(str1 + 2, str1, 6); 00341013 mov eax,dword ptr [str1 (343018h)] 00341018 mov dword ptr [str1+2 (34301Ah)],eax // put 4 bytes to destination, earlier than the above example 0034101D mov cx,word ptr [str1+4 (34301Ch)] // HA, new register! Holding a word, which is exactly the left 2 bytes (after 4 bytes loaded to %eax) printf("New string: %s\n", str1); 00341024 push offset str1 (343018h) 00341029 push offset string "New string: %s\n" (342104h) 0034102E mov word ptr [str1+6 (34301Eh)],cx // Again, pulling the stored word back from the new register 00341035 call esi
вот почему я всегда выбираю
memmove
при попытке скопировать 2 перекрывающихся блоков памяти.
разницу между
memcpy
иmemmove
это
на
memmove
, исходная память указанного размера копируется в буфер и затем перемещается в пункт назначения. Так что если память перекрывается, нет никаких побочных эффектов.в случае
memcpy()
, нет никакого дополнительного буфера, взятого для исходной памяти. Копирование выполняется непосредственно в память, так что, когда есть перекрытие памяти, мы получаем неожиданные результаты.этими можно наблюдать с помощью следующего кода:
//include string.h, stdio.h, stdlib.h int main(){ char a[]="hare rama hare rama"; char b[]="hare rama hare rama"; memmove(a+5,a,20); puts(a); memcpy(b+5,b,20); puts(b); }
вывод:
hare hare rama hare rama hare hare hare hare hare hare rama hare rama
как уже отмечалось в других ответах,
memmove
является более сложным, чемmemcpy
такой, что он учитывает перекрытия памяти. Результат memmove определяется как если быsrc
был скопирован в буфер, а затем буфер скопирован вdst
. Это не означает, что фактическая реализация использует какой-либо буфер, но, вероятно, выполняет некоторую арифметику указателя.
компилятор может оптимизировать memcpy, например:
int x; memcpy(&x, some_pointer, sizeof(int));
этот memcpy может быть оптимизирован как:
x = *(int*)some_pointer;
код, указанный в ссылках http://clc-wiki.net/wiki/memcpy для memcpy, кажется, немного смущает меня, так как он не дает тот же результат, когда я реализовал его, используя приведенный ниже пример.
#include <memory.h> #include <string.h> #include <stdio.h> char str1[11] = "abcdefghij"; void *memcpyCustom(void *dest, const void *src, size_t n) { char *dp = (char *)dest; const char *sp = (char *)src; while (n--) *dp++ = *sp++; return dest; } void *memmoveCustom(void *dest, const void *src, size_t n) { unsigned char *pd = (unsigned char *)dest; const unsigned char *ps = (unsigned char *)src; if ( ps < pd ) for (pd += n, ps += n; n--;) *--pd = *--ps; else while(n--) *pd++ = *ps++; return dest; } int main( void ) { printf( "The string: %s\n", str1 ); memcpy( str1 + 1, str1, 9 ); printf( "Actual memcpy output: %s\n", str1 ); strcpy_s( str1, sizeof(str1), "abcdefghij" ); // reset string memcpyCustom( str1 + 1, str1, 9 ); printf( "Implemented memcpy output: %s\n", str1 ); strcpy_s( str1, sizeof(str1), "abcdefghij" ); // reset string memmoveCustom( str1 + 1, str1, 9 ); printf( "Implemented memmove output: %s\n", str1 ); getchar(); }
выход :
The string: abcdefghij Actual memcpy output: aabcdefghi Implemented memcpy output: aaaaaaaaaa Implemented memmove output: aabcdefghi
но теперь вы можете понять, почему memmove позаботится о перекрытии вопрос.
я попытался запустить ту же программу с помощью 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