Подтвердить что ты не робот

Memcpy vs for loop - Каков правильный способ скопировать массив из указателя?

У меня есть функция foo(int[] nums), которая, как я понимаю, по существу эквивалентна foo(int* nums). Внутри foo Мне нужно скопировать содержимое массива, на которое указывает nums, в некоторый int[10], объявленный в рамках foo. Я понимаю, что недопустимо следующее:

void foo (int[] nums) 
{
    myGlobalArray = *nums
}

Каков правильный способ копирования массива? Должен ли я использовать memcpy следующим образом:

void foo (int[] nums)
{
    memcpy(&myGlobalArray, nums, 10);
}

или я должен использовать цикл for?

void foo(int[] nums)
{
    for(int i =0; i < 10; i++)
    {
        myGlobalArray[i] = nums[i];
    }
}

Есть ли третий вариант, который мне не хватает?

4b9b3361

Ответ 1

Memcpy, вероятно, будет быстрее, но, скорее всего, вы сделаете ошибку, используя его. Это может зависеть от того, насколько разумным является ваш оптимизирующий компилятор.

Однако ваш код неверен. Это должно быть:

memcpy(&myGlobalArray, nums, 10 * sizeof(int) );

Ответ 2

Да, третий вариант - использовать конструкцию С++:

std::copy(&nums[0], &nums[10], myGlobalArray);

С любым компилятором sane:

  • должен быть оптимальным в большинстве случаев (скомпилируется по возможности memcpy()),
  • безопасен по типу,
  • изящно справляется, когда вы решите изменить тип данных на не-примитивный (т.е. он вызывает конструкторы копирования и т.д.),
  • изящно справляется, когда вы решите перейти на класс контейнера.

Ответ 3

В общем случае сценарий наихудшего сценария будет в не оптимизированной сборке отладки, где memcpy не является встроенным и может выполнять дополнительные проверки на работоспособность/подтверждение, составляющие небольшое количество дополнительных инструкций против цикла for.

Однако memcpy обычно хорошо применяется для использования таких вещей, как intrinsics и т.д., но это будет зависеть от целевой архитектуры и компилятора. Маловероятно, что memcpy будет когда-либо хуже, чем реализация for-loop.

Люди часто сталкиваются с тем, что размеры memcpy в байтах, и они пишут такие вещи:

// wrong unless we're copying bytes.
memcpy(myGlobalArray, nums, numNums);
// wrong if an int isn't 4 bytes or the type of nums changed.
memcpy(myGlobalArray, nums, numNums);
// wrong if nums is no-longer an int array.
memcpy(myGlobalArray, nums, numNums * sizeof(int));

Вы можете защитить себя здесь, используя языковые функции, которые позволяют вам сделать некоторую степень отражения, то есть: делать что-то с точки зрения самих данных, а не с тем, что вы знаете о данных, потому что в общей функции вы вообще не используете " t знать что-либо о данных:

void foo (int[] nums, size_t numNums)
{
    memcpy(myGlobalArray, nums, numNums * sizeof(*nums));
}

Обратите внимание, что вы не хотите "&" infront "myGlobalArray", потому что массивы автоматически распадаются на указатели; вы фактически копировали "nums" в адрес в памяти, где удерживался указатель на myGlobalArray [0].

Использование memcpy для объектов может быть опасным, рассмотрите:

struct Foo {
    std::string m_string;
    std::vector<int> m_vec;
};

Foo f1;
Foo f2;
f2.m_string = "hello";
f2.m_vec.push_back(42);
memcpy(&f1, &f2, sizeof(f2));

Это НЕПРАВИЛЬНЫЙ способ копирования объектов, которые не являются POD (простые старые данные). И f1, и f2 теперь имеют std::string, который считает, что он владеет "привет". Один из них собирается сбой, когда они разрушают, и оба они считают, что они имеют один и тот же вектор целых чисел, который содержит 42.

Лучшей практикой для программистов на С++ является использование std::copy:

std::copy(nums, nums + numNums, myGlobalArray);

Это может сделать время принятия решения о том, что делать, включая использование memcpy или memmove и потенциально используя SSE/векторные инструкции, если это возможно. Другим преимуществом является то, что если вы пишете это:

struct Foo {
    int m_i;
};

Foo f1[10], f2[10];
memcpy(&f1, &f2, sizeof(f1));

а затем сменив Foo на включение std::string, ваш код сломается. Если вы вместо этого пишете:

struct Foo {
    int m_i;
};

enum { NumFoos = 10 };
Foo f1[NumFoos], f2[NumFoos];
std::copy(f2, f2 + numFoos, f1);

компилятор переключит ваш код, чтобы делать правильные вещи без какой-либо дополнительной работы для вас, а ваш код немного читабельнее.

Ответ 4

Для производительности используйте memcpy (или эквиваленты). Он очень оптимизировал платформенный код для быстрого шунтирования данных.

Для удобства обслуживания рассмотрите, что вы делаете - цикл for может быть более читабельным и понятным. (Получение неверного memcpy - быстрый путь к сбою или еще хуже)

Ответ 5

По существу, если вы имеете дело с типами POD (Plain Ol 'Data), такими как int, unsigned int, указатели, структуры данных и т.д., вы можете использовать mem *.

Если ваш массив содержит объекты, используйте цикл for, поскольку для обеспечения правильного назначения может потребоваться оператор =.