Как реализовать memmove в стандарте C без промежуточной копии?

Со страницы руководства в моей системе:

void * memmove (void * dst, const void * src, size_t len);

ОПИСАНИЕ
Функция memmove () копирует len байтов из строки src в строку dst.
Две строки могут перекрываться; копия всегда делается неразрушающим
манера.

Из стандарта C99:

6.5.8.5 Когда сравниваются два указателя, результат зависит от относительного расположения в адресном пространстве указанных объектов. Если два указателя на объект или неполный тип оба указывают на один и тот же объект, или оба указывают один за последним элементом одного и того же объекта массива, они сравниваются равными. Если указанные объекты являются членами одного и того же агрегатного объекта, указатели на элементы структуры, объявленные позже, сравниваются больше, чем указатели на элементы, объявленные ранее в структуре, а указатели на элементы массива с большими значениями нижнего индекса сравниваются больше, чем указатели на элементы того же массива с более низкими значениями индекса. Все указатели на члены одного и того же объекта объединения сравниваются одинаково. Если выражениеP указывает на элемент объекта массива, а выражение Q указывает на последний элемент того же объекта массива, выражение указателяQ+1 сравнивает больше чемP, Во всех остальных случаях поведениене определено.

Акцент мой.

Аргументыdst а такжеsrc можно преобразовать в указатели наchar чтобы устранить строгие проблемы с алиасами, но возможно ли сравнить два указателя, которые могут указывать внутри разных блоков, чтобы сделать копию в правильном порядке, если они указывают внутри одного и того же блока?

Очевидное решениеif (src < dst), но это не определено, еслиsrc а такжеdst указать на разные блоки. «Не определено» означает, что вы даже не должны предполагать, что условие возвращает 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 предоставляется вместе с компилятором. На практике авторы компилятора могут просто способствовать сопоставлению неопределенного указателя с неопределенным поведением или использовать соответствующую прагму, чтобы заставить их компилятор скомпилировать их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);
    }

Я все еще хотел бы использовать этот пример в качестве доказательства того, что стандарт заходит слишком далеко с неопределенным поведением, если это правда, чтоmemmove не может быть эффективно реализовано в стандарте C. Например, никто не отмеченэтот ТАК вопрос.

Ответы на вопрос(1)

Ваш ответ на вопрос