Относительно внедрения memmove

Я просматривал общедоступные реализации на wikibooks.org. Он реализует memmove() следующим образом, явно заявляя, что он «не полностью переносим»! Мне было интересно, почему:

  1. круглые скобки были помещены в первую строку кода, и
  2. код не является полностью переносимым.

Код выглядит следующим образом:

void *(memmove)(void *s1, const void *s2, size_t n)
{
   char *p1 = s1;
   const char *p2 = s2;

   if (p2 < p1 && p1 < p2 + n) {
       /* do a descending copy */
       p2 += n;
       p1 += n;
       while (n-- != 0)
           *--p1 = *--p2;
   } else
       while (n-- != 0)
           *p1++ = *p2++;

   return s1;
}

person Quiescent    schedule 26.10.2013    source источник
comment
Хороший вопрос. Прочитайте Что означают скобки вокруг имя функции означает?   -  person Grijesh Chauhan    schedule 26.10.2013
comment
Спасибо, было полезно!   -  person Quiescent    schedule 26.10.2013


Ответы (2)


Спецификация функции memmove() заключается в том, что она может обрабатывать перекрывающиеся источник и место назначения, но в спецификации не говорится, что memmove() должна вызываться с указателями на один и тот же блок памяти («объект» на языке стандарта).

Когда p1 и p2 являются указателями на разные блоки памяти, условие p2 < p1 является неопределенным поведением. Стандарт C99 гласит (6.5.8:5):

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

Я не знаю, к чему относится объяснение, но это один из определенных источников непереносимости.

Другая реализация может использовать (uintptr_t)p2 < (uintptr_t)p1. Тогда сравнение < является сравнением целых чисел. Преобразование в uintptr_t дает результаты, определенные реализацией. Тип uintptr_t был введен в C99 и представляет собой целочисленный тип без знака, который гарантированно содержит представление указателя.

Полностью переносимая реализация memmove() может использовать третий буфер для хранения промежуточной копии или использовать == сравнение (которое дает указанные результаты в том контексте, в котором оно должно быть использовано).

person Pascal Cuoq    schedule 26.10.2013

  1. Объяснение круглых скобок здесь: скобки вокруг имени функции означают?

  2. Это не переносимо из-за сравнений p2 < p1 и p1 < p2 + n. Стандарт C определяет поведение при сравнении указателей только тогда, когда два указателя указывают на один и тот же объект. Этот код зависит от их разумной работы, даже когда вы копируете между разными объектами.

В практическом смысле код в порядке. Когда указатели не указывают на один и тот же объект, не имеет значения, выполняется ли копирование по возрастанию или по убыванию, поэтому результаты сравнений не имеют значения. Важно только то, что код не делает ничего ужасного, например, не приводит к сбою процесса или не отправляет коды запуска ядерного оружия. Стандарт C не запрещает это, но это маловероятно в какой-либо реалистичной реализации. Большинство реализаций просто сравнивают необработанные адреса, а любые странные реализации просто возвращают непредсказуемое значение, но не имеют побочных эффектов.

person Barmar    schedule 26.10.2013
comment
Код подходит для современной платформы с плоским адресным пространством. У Стива Джессопа есть хорошее объяснение std::less С++ в его ответе на мой старый вопрос (на который я ссылался в своем ответе на этот вопрос). - person Pascal Cuoq; 26.10.2013