В чем разница между 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 ---]затем копирование первого байта от
srcdodstуже уничтожает содержимое последних байтов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