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));

Ссылки

См. также