C ++ функция memcpy, зачем использовать * s ++ вместо s [i] - PullRequest
2 голосов
/ 16 февраля 2012

Я пишу memcpy с нуля, и я искал реализации других людей ... Моя реализация:

void* memcpy (void *destination, const void *source, size_t num)
{
    char *D = (char*)destination;
    char *S = (char*)source;
    for(int i = 0; i < num; i++)
            D[i] = S[i];
    return D;
}

различные другие источники и ссылки, которые я исследовал, имеют

void* memcpy (void *destination, const void *source, size_t num)
{
    char *D = (char*)destination;
    char *S = (char*)source;
    for(int i = 0; i < num; i++) 
    {
            *D = *S;
            D++;
            S++;
    }
    return D;
}

У меня проблемы с пониманием разницы и того, будут ли они давать разные результаты.Особенно меня смущает D ++;и S ++;

Ответы [ 8 ]

2 голосов
/ 16 февраля 2012

Современные компиляторы оптимизируют их для одного и того же кода. Это называется снижение силы. (За исключением разных возвращаемых значений.)

1 голос
/ 16 февраля 2012

D++ и S++ увеличивает указатель.

Имейте в виду, что D[i] эквивалентно *(D + i).

Таким образом, один увеличивает указатель, другой сохраняет базу и добавляет смещение.

Современные компиляторы, вероятно, скомпилируют тот же код.

Примечание: я предполагаю, что return D; во втором примере является ошибкой копирования-вставки, как и должно быть return destination;, потому что D - это приращение и указание на память "после" целевых байтов.

0 голосов
/ 16 февраля 2012

Хотя в любом случае это не имеет большого значения, я был бы немного удивлен, увидев любой из них. Я ожидаю что-то ближе к:

void* memcpy (void *destination, const void *source, size_t num) {
    char *S = (char *)source;
    char *D = (char *)destination;
    while (--num)
        *D++ = *S++;
    return destination;
}

Большинство приличных компиляторов будут генерировать примерно одинаковый код независимо. Я недавно не проверял, но в свое время большинство компиляторов, нацеленных на x86, превратили бы большую часть цикла в одну rep movsd инструкцию. Хотя они могут больше не быть - это уже не оптимально.

0 голосов
/ 16 февраля 2012

Вот внутренние циклы, составленные GCC. Я просто добавил restrict ключевые слова, удалил возвращаемое значение и скомпилировал 32-битную версию для Core 2:

Первый, версия массива:

.L3:
  movzbl  (%edi,%edx), %ecx
  addl    $1, %eax  
  cmpl    %ebx, %eax
  movb    %cl, (%esi,%edx)
  movl    %eax, %edx
  jne .L3

Второй, инкрементная версия:

.L9:
  movzbl  (%edx), %ebx
  addl    $1, %ecx   
  addl    $1, %edx   
  movb    %bl, (%eax)
  addl    $1, %eax  
  cmpl    %ecx, %esi
  ja  .L9

Компилятор, как вы можете видеть, просматривал обе конструкции.

0 голосов
/ 16 февраля 2012

Что вас смущает, так это арифметика указателя: D++, S++.Указатели увеличиваются для ссылки на следующие char (как они char*)

0 голосов
/ 16 февраля 2012

Хотя оба семантически означают одно и то же, версия *s++ избавит вас от необходимости смещать начальное значение указателя в виде вашего приращения через байты массива, который вы копируете. Другими словами, «базовое» представление s[i] на самом деле *(s + i*sizeof(type)), а умножения, особенно больших значений i, намного медленнее простых приращений на маленькие значения, по крайней мере, в зависимости от архитектуры машины.

В конце концов, реализация libc в *1007* будет намного быстрее, чем все, что вы могли бы написать вручную в C, из-за использования машинно-зависимых оптимизированных инструкций по копированию памяти, которые вы не можете сознательно доступ через обычный C-код.

0 голосов
/ 16 февраля 2012

Алгоритмы эквивалентны. Вторая версия использует арифметику указателей для перемещения указателей на следующую позицию вместо индексации массивов с использованием синтаксиса a[i].

Это работает, потому что a[i] на самом деле является сокращением для *(a+i) (читай: продвижение i после a и считывание значения в этом месте). Вместо выполнения полного смещения (+i) на каждой итерации он выполняет частичное смещение (++a) на каждой итерации и накапливает результат.

0 голосов
/ 16 февраля 2012

На момент написания этого кода, вероятно, было быстрее избежать добавления индекса в указатели.

В моем собственном тестировании архитектуры x86, в которой режим индексации встроен в низкоуровневые инструкции, индексированный метод немного быстрее.

...