Я написал функцию, которая копирует память из одного места назначения в другое. Я бы хотел, чтобы вы посмотрели мой код и сказали, нужно ли мне что-то исправить. Это код:
void* memCopy(void* destination, const void* source, size_t size)
{
double *a = (double*)source, *b = (double*)destination;
void* end = a + (size / sizeof(double));
while ((void*)a < end)
{
*b = *a;
++a;
++b;
}
char* a2 = (char*)a;
char* b2 = (char*)b;
end = a2 + (size % sizeof(double));
while (a2 < end)
{
*b2 = *a2;
++a2;
++b2;
}
return destination;
}
я использовал double* чтобы сделать функцию быстрее.
2 ответа
Ваш код не компилируется чисто с GCC:
move.c: In function 'memCopy':
move.c:16:15: warning: comparison of distinct pointer types lacks a cast
while (a2 < end)
^
Также 2 кратких момента:
- Зачем изобретать велосипед? Большинство реализаций memcpy, memmove и т. Д. В стандартных библиотеках будут правильно и эффективно реализованы, во многих случаях без использования чистого C.
- Ваш код предполагает, что и источник, и данные правильно выровнены для доступа как double. Если это не так, то в зависимости от платформы код может а) работать плохо, б) давать неверные результаты или в) просто давать сбой, если я не ошибаюсь. Он работает (очевидно, правильно) на моем ПК, но это не гарантия переносимости.
Реализация FreeBSD показывает, как это можно сделать, предполагая, что отображение указателей на uintptr_t дает значения, которые можно соответствующим образом замаскировать — это верно для соответствующих целей кода, но не может быть универсальным.
В дополнение к хорошему ответу @Mark Bluemel:
Перекрывать
Разница между memcpy() а также memmove() это возможность обрабатывать перекрывающиеся буферы. Обратите внимание на ключевое слово restrict.
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
void *memmove(void *s1, const void *s2, size_t n);
restrict примерно означает, что доступ к буферу не зависит от других действий.
memCopy() код не копирует хорошо перекрывающиеся буферы, когда source предшествует destination. Это больше похоже на
void* memCopy(void* restrict destination, const void* restrict source, size_t size)
Используя restrict, компилятор может вызывать дополнительные оптимизации.
Решение действовать как memmove() включает зацикливание вверх или вниз в зависимости от того, что больше source или же destination. Хитрость в том, что это сравнение величия указателя, безусловно, невозможно в C. memmove(), как библиотечная функция, имеет доступ к информации, которой нет у C. Тем не менее, сравнение указателей, преобразованных в целое число, обычно достаточный.
Типы
double является нет путь идти. *b = *a; не гарантируется копирование битовой комбинации или потенциальное инициирование исключения сигнализации не числа. Лучше избегать проблем с плавающей запятой. Вместо этого используйте широкий беззнаковый целочисленный тип, возможно uintmax_t, unsigned long long, или же uint64_t. (Плюсы и минусы каждого. Я бы выбрал uint64_t когда возможно.)
На древних машинах, *b2 = *a2; может вызвать ловушка. Вместо этого используйте unsigned char *a2; unsigned char *b2;
Нулевой размер
memCopy(..., ..., 0) правильно обрабатывается. Браво! — здесь удалось избежать распространенной ошибки.

Да, точно. Я предполагаю, что Иван тестировал некоторую форму x86, которая очень снисходительна к невыровненному доступу. Большинство процессоров в лучшем случае будут отказываться от шины.
— Тоби Спейт
Марк, хм, FreeBSD использует
if ((unsigned long)dst < (unsigned long)src) {. Я ожидалif ((uintptr_t)dst < (uintptr_t)src) {, что это ошибка, когдаuintptr_tшире, чемunsigned long.— chux — Восстановить Монику
@ chux-ReinstateMonica Я сомневаюсь, что uintptr_t был даже блеском в глазах разработчика C, когда этот код был разработан — он довольно древний. Я подозреваю, что пока он не выйдет из строя, все останется как есть. Как упоминается в вашем ответе, он действительно имеет дело с перекрытиями путем соответствующего направленного копирования. Я не стал комментировать использование двойного слова, как вы, поскольку я не был полностью уверен в своей правоте, но я согласен с вашими опасениями.
— Марк Блюмел
@MarkBluemel Моя рекомендация не вводит
uintptr_tиспользование, поскольку связанный код уже используетuintptr_tвif ((t | (uintptr_t)dst) & wmask) {. К сожалению, он не используетuintptr_tравномерно.— chux — Восстановить Монику