Как реализовать 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. Например, никто не отмеченэтот ТАК вопрос.