Со страницы руководства в моей системе:
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)
, но оно не определено, если 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. Например, никто не ставил галочку, когда отвечал на этот вопрос SO .