В чем разница между memmove и memcpy?
в чем разница между memmove
и memcpy
? Какой вы обычно используете и как?
11 ответов:
С
memcpy
, назначение не может перекрывать источник вообще. Сmemmove
он может. Это значит, чтоmemmove
может быть очень немного медленнее, чемmemcpy
, Так как он не может принять те же предположения.например,
memcpy
всегда можно скопировать адреса с низкого на высокий. Если назначение перекрывается после источника, это означает, что некоторые адреса будут перезаписаны перед копированием.memmove
обнаружил бы это и копировал в другом направлении - от высокого до низкого - в этом случае. Однако проверка этого и переключение на другой (возможно, менее эффективный) алгоритм требует времени.
memmove
может обрабатывать перекрывающиеся памяти,memcpy
не могу.считают
char[] str = "foo-bar"; memcpy(&str[3],&str[4],4); //might blow up
очевидно, что источник и пункт назначения теперь перекрываются, мы перезаписываем "бар" с "БАР". Это неопределенное поведение с помощью
memcpy
если источник и назначения перекрываются, так что в этом случае нам нужноmemmove
.memmove(&str[3],&str[4],4); //fine
основное различие между
memmove()
иmemcpy()
, что вmemmove()
a буфер - временная память-используется, поэтому нет риска перекрытия. С другой стороны,memcpy()
непосредственно копирует данные из местоположения, на которое указывает источник к месту, указанному назначения. (http://www.cplusplus.com/reference/cstring/memcpy/)рассмотрим следующий примеры:
#include <stdio.h> #include <string.h> int main (void) { char string [] = "stackoverflow"; char *first, *second; first = string; second = string; puts(string); memcpy(first+5, first, 5); puts(first); memmove(second+5, second, 5); puts(second); return 0; }
как вы и ожидали, это будет распечатать:
stackoverflow stackstacklow stackstacklow
но в этом примере, результаты не будут такими же:
#include <stdio.h> #include <string.h> int main (void) { char string [] = "stackoverflow"; char *third, *fourth; third = string; fourth = string; puts(string); memcpy(third+5, third, 7); puts(third); memmove(fourth+5, fourth, 7); puts(fourth); return 0; }
выход:
stackoverflow stackstackovw stackstackstw
это потому, что "memcpy ()" делает следующее:
1. stackoverflow 2. stacksverflow 3. stacksterflow 4. stackstarflow 5. stackstacflow 6. stackstacklow 7. stackstacksow 8. stackstackstw
просто от стандарта ИСО / ИЭК: 9899 он хорошо описан.
7.21.2.1 функция memcpy
[...]
2 Функция memcpy копирует n символов из объекта, на который указывает s2, в объект, на который указывает s1. если копирование происходит между объектами, которые перекрываются, поведение не определено.
и
7.21.2.2 функция memmove
[...]
2 Функция memmove копирует n символов из объекта, на который указывает s2, в объект, на который указывает s1. Копирование происходит как если бы n символов из объекта на которые указывает s2, сначала копируются во временный массив из n символов, который не имеет перекрытие объекты, на которые указывают s1 и s2, а затем n символов из списка временные массивы копируются в объект, на который указывает s1.
какой я обычно используют в соответствии с вопросом, зависит от того, какая функциональность мне нужна.
обычный текст
memcpy()
не даетs1
иs2
перекрытие, в то время какmemmove()
делает.
предполагая, что вам придется реализовать оба, реализация может выглядеть так:
void memmove ( void * dst, const void * src, size_t count ) { if ((uintptr_t)src < (uintptr_t)dst) { // Copy from back to front } else if ((uintptr_t)dst < (uintptr_t)src) { // Copy from front to back } } void mempy ( void * dst, const void * src, size_t count ) { if ((uintptr_t)src != (uintptr_t)dst) { // Copy in any way you want } }
и это должно довольно хорошо объяснить разницу.
memmove
всегда копирует таким образом, что это все еще безопасно, еслиsrc
иdst
перекрытие, в то время какmemcpy
просто не волнует, как говорится в документации при использованииmemcpy
, две области памяти не должен перекрытие.например, если
memcpy
копирует "спереди назад" и блоки памяти выровнял как это[---- src ----] [---- dst ---]
затем копирование первого байта от
src
dodst
уже уничтожает содержимое последних байтовsrc
прежде чем они были скопированы. Так что здесь можно только безопасно скопировать "задом наперед".поменяйтесь
src
иdst
:[---- dst ----] [---- src ---]
в этом случае безопасно копировать только "спереди назад", так как копирование" сзади вперед " уничтожит
src
рядом с его фронтом уже при копировании первого байта.вы возможно, заметили, что
memmove
реализация выше даже не проверяет, действительно ли они перекрываются, она просто проверяет их относительные положения, но это само по себе сделает копию безопасной. Какmemcpy
обычно использует самый быстрый способ копирования памяти в любой системе,memmove
обычно реализуется как:void memmove ( void * dst, const void * src, size_t count ) { if ((uintptr_t)src < (uintptr_t)dst && (uintptr_t)src + count > (uintptr_t)dst) { // Copy from back to front } else if ((uintptr_t)dst < (uintptr_t)src && (uintptr_t)dst + count > (uintptr_t)src ) { // Copy from front to back } else { // They don't overlap for sure memcpy(dst, src, count); } }
иногда, если
memcpy
всегда копирует "спереди назад" или "сзади вперед",memmove
могут также использоватьmemcpy
в одном из пересекающихся дел, ноmemcpy
возможно, даже копия по-разному в зависимости от того, как данные выровнены и/или сколько данных должно быть скопировано, поэтому даже если вы проверили, какmemcpy
копии в вашей системе, вы не можете полагаться на то, что результат теста будет всегда правильным.что это значит для вас, когда вы решаете, какой из них позвонить?
если вы не знаете наверняка, что
src
иdst
не пересекаются, называютmemmove
как это всегда приведет к правильным результатам, и обычно так же быстро, как это возможно случай копирования вам требуется.если вы точно знаете, что
src
иdst
не пересекаются, называютmemcpy
поскольку не имеет значения, какой из них вы вызываете для результата, оба будут работать правильно в этом случае, ноmemmove
никогда не будет быстрее, чемmemcpy
и если вам не повезло, это может быть даже медленнее, так что вы можете только выиграть вызовmemcpy
.
Я пробовал работать над кодом: основная причина, по которой я хочу знать разницу
memcpy
иmemmove
.#include <stdio.h> #include <string.h> int main (void) { char string [] = "stackoverflow"; char *third, *fourth; third = string; fourth = string; puts(string); memcpy(third+5, third, 7); puts(third); memmove(fourth+5, fourth, 7); puts(fourth); return 0; }
дает ниже выхода
stackoverflow stackstackovw stackstackstw
Теперь Я заменил
memmove
Сmemcpy
и я получил тот же вывод#include <stdio.h> #include <string.h> int main (void) { char string [] = "stackoverflow"; char *first, *second; first = string; second = string; puts(string); memcpy(first+5, first,7); puts(first); memcpy(second+5, second, 7); puts(second); return 0; }
выход:
stackoverflow stackstackovw stackstackstw
char string [] = "stackoverflow";
char *first, *second; first = string; second = string; puts(string);
o / p - stackoverflow
memcpy(first+5, first,7); puts(first);
здесь 7 символов, указанных вторым т. е. "stackov" вставляется в +5 позиции в результате
o / p - stackstackovw
memcpy(second+5, second, 7); puts(second);
здесь входная строка- "stackstackovw", 7 символов, указываемых секундой (т. е." stackst"), копируется в буфер, а затем вставляется в место +5, что приводит к
o/p-stackstackstw
0-----+5
stack stackst Вт
есть два очевидных способа реализации
mempcpy(void *dest, const void *src, size_t n)
(игнорируя возвращаемое значение):
for (char *p=src, *q=dest; n-->0; ++p, ++q) *q=*p;
char *p=src, *q=dest; while (n-->0) q[n]=p[n];
в первой реализации копия переходит от низкого к высокому адресам, а во второй-от высокого к низкому. Если копируемый диапазон перекрывается (как это происходит, например, при прокрутке буфера кадров), то только одно направление операции является правильным, а другое будет перезаписывать местоположения, которые впоследствии будут читай дальше.
A
memmove()
реализация, в самом простом виде, будет тестироватьdest<src
(в некотором зависящем от платформы способе), и выполните соответствующее направлениеmemcpy()
.пользовательский код не может этого сделать, конечно, потому что даже после кастинга
src
иdst
к некоторому конкретному типу указателя, они не указывают (вообще) в такой же объект и поэтому не могут быть сравнены. Но стандартная библиотека может иметь достаточно знаний о платформе, чтобы выполнить такое сравнение, не вызывая Неопределенное Поведение.
обратите внимание, что в реальной жизни реализации, как правило, значительно сложнее, чтобы получить максимальную производительность от больших передач (когда выравнивание позволяет) и/или хорошее использование кэша данных. Код выше-это просто сделать точку как можно проще.
memmove может иметь дело с перекрывающимися областями источника и назначения, в то время как memcpy не может. Среди двух, memcpy является гораздо более эффективным. Так что, лучше использовать memcpy, если вы можете.
Ссылка:https://www.youtube.com/watch?v=Yr1YnOVG-4g доктор Джерри Кейн, (Stanford Intro Systems Lecture-7) Время: 36: 00