memcpy
memcpy (от англ. memory copy — копирование памяти) — функция стандартной библиотеки языка программирования Си, копирующая содержимое одной области памяти в другую.
Функция определена в заголовочном файле string.h (а также в mem.h), описывается в стандартах ANSI C и POSIX.
memcpy_s
Чтобы не выполнять лишних действий, функция memcpy() не проверяет соответствие размера выходного буфера количеству копируемых байт, возлагая эту обязанность на программиста. В результате совершается достаточно много ошибок, способных привести к переполнению буфера.
Поэтому ближе к концу 2009 г. компания Microsoft добавила memcpy(), CopyMemory() и RtlCopyMemory() в список функций, запрещённых в соответствии с методикой разработки безопасных программ Secure Development Lifecycle (SDL). Те разработчики, которые хотят создавать совместимые с SDL приложения, должны будут использовать вместо memcpy() функцию memcpy_s, позволяющую указывать размер буфера. Функция memcpy_s() непереносима и не включена в стандарт Си.
Определение
void *memcpy(void *dst, const void *src, size_t n);
где:
- dst — адрес буфера назначения
- srс — адрес источника
- n — количество байт для копирования
Функция копирует n байт из области памяти, на которую указывает src, в область памяти, на которую указывает dst. Функция возвращает адрес назначения dst.
Области памяти не должны перекрываться , иначе данные могут быть скопированы неправильно, например таким образом:
__src___
| |
1234567890xxxxx
|__ ___|
dst
после копирования буфер dst содержит данные отличные от исходных, так как они были разрушены в процессе копирования:
__dst___
| |
121212121212xxx
Что получится на самом деле, зависит от реализации функции (пример относится к одной из реализации приведённых ниже).
Для правильного копирования перекрывающихся областей нужно использовать функцию memmove(). Некоторые реализации memcpy() (например в libc FreeBSD и OpenBSD) делают то же что и memmove(), принуждая работать правильно даже неправильно написанную программу, однако при написании переносимой программы на это надеяться нельзя.
Алгоритм работы и реализации
memcpy() копирует содержимое src в буфер dst, например, так:
int i;
for( i = 0; i < n; i++ )
((unsigned char*)dst)[i] = ((unsigned char*)src)[i];
return dst;
Но данный пример будет работать медленнее, чем любые практические реализации, так как они оптимизированы:
- Не используют индексы, как в примере.
- Перемещают за один цикл не один байт, а блок, равный машинному слову (2, 4 или 8 байт; 16, 32 или 64 бит), которое копируется процессором за то же время, что и байт. Такой подход наиболее эффективен при копировании данных, выровненных на границу машинного слова.
- Иногда используют инструкции процессора для работы с блоками данных (movsX для i386) в этом случае функция пишется с применением языка ассемблера
Пример частично оптимизированной версии:
int i, m;
unsigned long *wdst = dst; // текущая позиция в буфере назначения
unsigned long *wsrc = src; // текущая позиция в источнике
unsigned char *cdst, *csrc;
for(i = 0, m = n / sizeof(long); i < m; i++) // копируем основную часть блоками по 4 или 8 байт
*(wdst++) = *(wsrc++); // (в зависимости от платформы)
cdst = (unsigned char*)wdst;
csrc = (unsigned char*)wsrc;
for(i = 0, m = n % sizeof(long); i < m; i++) // остаток копируем побайтно
*(cdst++) = *(csrc++);
return dst;
Данная версия копирует 4 или 8 байт (размер типа long равен 32 битам) за цикл, но не проверяет выровненность данных.
Пример использования
#include <string.h>
unsigned int array[512]; // источник
unsigned char byte_array[sizeof(array) * sizeof(unsigned int)]; // буфер назначения
memcpy(byte_array, array, sizeof(byte_array));
Ссылки
- Описание функции memcpy() в стандарте POSIX (англ.)
- Описание функции memcpy в составе Visual C++ (англ.)
- Сравнение скорости работы memcpy и цикла на C (англ.)
- Патч в ядре Linux, оптимизирующий memcpy (англ.)