В чем разница между memmove
и memcpy
? Какой из них вы обычно используете и как?
В чем разница между memmove и memcpy?
Ответы (9)
С memcpy
пункт назначения вообще не может перекрывать источник. С memmove
может. Это означает, что memmove
может быть немного медленнее, чем memcpy
, поскольку он не может делать те же предположения.
Например, memcpy
может всегда копировать адреса снизу вверх. Если место назначения перекрывается после источника, это означает, что некоторые адреса будут перезаписаны перед копированием. memmove
обнаружит это и скопирует в другом направлении - от максимума к минимуму - в этом случае. Однако проверка этого и переключение на другой (возможно, менее эффективный) алгоритм требует времени.
i = i++ + 1
неопределенным; компилятор не запрещает вам писать именно этот код, но результатом этой инструкции может быть что угодно, и разные компиляторы или процессоры будут показывать здесь разные значения.
- person Mecki; 15.04.2018
memmove
может обрабатывать перекрывающуюся память, memcpy
не может.
Учитывать
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
Очевидно, что теперь источник и место назначения перекрываются, мы заменяем «-bar» на «bar». Это неопределенное поведение с использованием memcpy
, если источник и место назначения перекрываются, поэтому в этом случае нам понадобится memmove
.
memmove(&str[3],&str[4],4); //fine
Предполагая, что вам придется реализовать и то, и другое, реализация может выглядеть так:
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 memcpy ( 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
в dst
уже уничтожает содержимое последних байтов 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
.
memmove
копирует данные спереди назад, если dst < src
в противном случае копирует данные спереди назад. Это безопасно, потому что если есть наложение, результат определен (и правильный), поэтому поведение безопасно, в противном случае, если наложения нет, результат не указан, но копирование в обоих направлениях безопасно.
- person VainMan; 24.09.2020
Основное различие между memmove()
и memcpy()
состоит в том, что в memmove()
используется буфер - временная память, поэтому нет риска перекрытия. С другой стороны, 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
memmove()
. Он имеет полное право перемещаться на месте (при условии, что каждое чтение завершается до любой записи по тому же адресу).
- person Toby Speight; 20.05.2016
Один (memmove
) обрабатывает перекрывающиеся места назначения, другой (memcpy
) - нет.
просто из стандарта ISO / IEC: 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()
допускает.
Есть два очевидных способа реализовать 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];
В первой реализации копирование переходит от младших адресов к старшим, а во второй - от старших к младшим. Если копируемый диапазон перекрывается (как, например, в случае прокрутки фреймбуфера), то только одно направление работы является правильным, а другое будет перезаписывать места, из которых впоследствии будут считываться.
Реализация memmove()
, в простейшем виде, будет проверять dest<src
(некоторым платформо-зависимым способом) и выполнять соответствующее направление memcpy()
.
Пользовательский код, конечно, не может этого сделать, потому что даже после преобразования src
и dst
в какой-то конкретный тип указателя они (как правило) не указывают на один и тот же объект, и поэтому их нельзя сравнивать. Но стандартная библиотека может иметь достаточно знаний о платформе для выполнения такого сравнения, не вызывая неопределенного поведения.
Обратите внимание, что в реальной жизни реализации имеют тенденцию быть значительно более сложными, чтобы получить максимальную производительность от более крупных передач (когда позволяет выравнивание) и / или хорошего использования кэша данных. Приведенный выше код предназначен для того, чтобы максимально упростить суть.
memmove может иметь дело с перекрывающимися областями источника и назначения, в то время как memcpy не может. Среди них memcpy намного эффективнее. Так что лучше ИСПОЛЬЗУЙТЕ memcpy, если можете.
Ссылка: https://www.youtube.com/watch?v=Yr1YnOVG-4g Д-р Джерри Кейн, (Стэнфордская вводная лекция по системам - 7) Время: 36:00
memcpy()
, а не memcopy()
.
- person chux - Reinstate Monica; 10.12.2016