На странице man в моей системе:
void * memmove (void * dst, const void * src, size_t len);
ОПИСАНИЕ
Функция memmove() копирует len байты из строки src в строку dst.
Две строки могут перекрываться; копия всегда выполняется в неразрушающем режиме манера.
Из стандарта C99:
6.5.8.5 Когда сравниваются два указателя, результат зависит от относительные местоположения в адресе пространство объектов, на которые указывает. Если два указателя на объект или неполный типы указывают на один и тот же объект, или оба указывают один за последний элемент одного и того же объекта массива, они равны. Если объекты указывающие на членов одного и того же совокупный объект, указатели на члены структуры, объявленные позже сравнить больше, чем указатели на участников, ранее заявленных в структура и указатели на массив элементы с большими значениями индекса сравнить больше, чем указатели на элементы того же массива с более низким индексы. Все указатели на члены одного и того же объекта объединения сравните равные. Если выражение
P
указывает на элемент массива объект и выражение Q указывает на последний элемент того же массива объект, выражение указателяQ+1
сравнивается большеP
. В целом в других случаях поведение undefined.
Акцент мой.
Аргументы dst
и src
могут быть преобразованы в указатели на char
, чтобы облегчить проблемы с строгим псевдонимом, но можно ли сравнить два указателя, которые могут указывать внутри разных блоков, чтобы сделать копию в правильном порядке в случае, если они указывают внутри одного и того же блока?
Очевидным решением является if (src < dst)
, но это undefined, если src
и dst
указывают на разные блоки. "Undefined" означает, что вы даже не должны предполагать, что условие возвращает 0 или 1 (это было бы названо "неуказанным" в стандартном словаре).
Альтернативой является if ((uintptr_t)src < (uintptr_t)dst)
, который по крайней мере не указан, но я не уверен, что стандарт гарантирует, что при src < dst
он эквивалентен (uintptr_t)src < (uintptr_t)dst)
. Сравнение указателей определяется из арифметики указателя. Например, когда я читал раздел 6.5.6 о добавлении, мне кажется, что арифметика указателя может идти в направлении, противоположном арифметике uintptr_t
, то есть, что совместимый компилятор может иметь, когда P
имеет тип char*
:
((uintptr_t)p)+1==((uintptr_t)(p-1)
Это только пример. Как правило, очень мало гарантируется при преобразовании указателей в целые числа.
Это чисто академический вопрос, потому что memmove
предоставляется вместе с компилятором. На практике авторы компилятора могут просто продвигать сравнение указателей undefined с неуказанным поведением или использовать соответствующую прагму, чтобы заставить их компилятор правильно компилировать их memmove
. Например, эта реализация имеет этот фрагмент:
if ((uintptr_t)dst < (uintptr_t)src) {
/*
* As author/maintainer of libc, take advantage of the
* fact that we know memcpy copies forwards.
*/
return memcpy(dst, src, len);
}
Я бы по-прежнему хотел бы использовать этот пример в качестве доказательства того, что стандарт заходит слишком далеко с поведением undefined, если верно, что memmove
не может быть эффективно реализовано в стандартном C. Например, никто не отметит, когда отвечает этот вопрос SO.