ПрограммированиеBackend C разработчик

Как реализовать безопасное копирование памяти между массивами в языке C? Какие функции стандартной библиотеки использовать, и в чем их отличие?

Проходите собеседования с ИИ помощником Hintsage

Ответ

История вопроса

Безопасное копирование памяти — одна из самых частых и критичных задач в C. Первоначально программы использовали цикл для побайтного копирования. Позже были добавлены стандартные функции: memcpy, memmove, а также более защищённые альтернативы вроде strncpy для строк. Неправильное копирование приводит к переполнениям буфера и уязвимостям.

Проблема

Использование неподходящей функции или неверного размера при копировании может разрушить данные или даже открыть путь интересующимся безопасности сторонним лицам. Отличить случаи, когда используется memmove, а когда допустимо memcpy, — принципиально важно для избегания ошибок с перекрывающейся областью памяти.

Решение

Для безопасного копирования нестроковой памяти используйте:

memcpy(dest, src, n);
  • memcpy быстро копирует n байт из src в dest, если их области памяти не перекрываются.
  • Если области могут перекрываться, используйте memmove:
memmove(dest, src, n);

Для копирования строк используйте либо strncpy, либо, лучше, модерновые и защищённые альтернативы, если доступны (strlcpy).

Ключевые особенности:

  • memcpy безопасен только при отсутствии перекрытия src и dest.
  • memmove корректно работает даже с перекрывающимися областями памяти.
  • Для строк лучше использовать специализированные функции, учитывая нулевой терминатор.

Вопросы с подвохом.

1. Чем отличается memcpy от memmove и когда какая функция необходима?

memcpy работает очень быстро, но не предназначен для перекрывающихся областей памяти — результат станет непредсказуемым. memmove гарантирует корректное копирование вне зависимости от перекрытия:

int arr[] = {1,2,3,4,5}; // memmove: безопасно копируем с перекрытием memmove(arr+1, arr, 4 * sizeof(int));

2. Безопасно ли использовать strcpy для копирования строки в меньший буфер?

Нет, функция strcpy не проверяет размер буфера назначения. Это часто приводит к переполнению буфера. Лучше воспользоваться strncpy, либо strlcpy (если есть), либо явно контролировать размер:

char dest[5]; strncpy(dest, src, sizeof(dest)-1); // src должен быть короче 5 символов

3. Можно ли использовать memcpy для копирования структур с указателями?

Да, но это копирует только сами указатели, а не данные по тем адресам. Это может привести к ошибкам при работе с памятью и двойному освобождению.

Типовые ошибки и анти-паттерны

  • Использование memcpy при перекрытии областей памяти.
  • Игнорирование размера буфера назначения.
  • Копирование структур, содержащих указатели, простым memcpy (shallow copy).

Пример из жизни

Негативный кейс

Копируется длинная строка в маленький буфер с помощью strcpy без проверки длины источника. Происходит переполнение буфера, что приводит к уязвимости.

Плюсы:

  • Минимальный код, быстрое выполнение.

Минусы:

  • Уязвимость безопасности, возможны аварийные завершения, повреждение памяти.

Позитивный кейс

Используется strncpy или memmove с точным контролем размера, вручную обеспечивается завершающий нулевой символ строки.

Плюсы:

  • Предсказуемое поведение, защищённость от переполнения буфера.

Минусы:

  • Требует дополнительного контроля длины и, возможно, небольшого уменьшения скорости работы из-за дополнительных проверок.