简体   繁体   中英

memcpy vs for loop - What's the proper way to copy an array from a pointer?

I have a function foo(int[] nums) which I understand is essentially equivalent to foo(int* nums) . Inside foo I need to copy the contents of the array pointed to by nums into some int[10] declared within the scope of foo . I understand the following is invalid:

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

What is the proper way to copy the array? Should I use memcpy like so:

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

or should I use a for loop?

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

Is there a third option that I'm missing?

Yes, the third option is to use a C++ construct:

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

With any sane compiler, it:

  • should be optimum in the majority of cases (will compile to memcpy() where possible),
  • is type-safe,
  • gracefully copes when you decide to change the data-type to a non-primitive (ie it calls copy constructors, etc.),
  • gracefully copes when you decide to change to a container class.

Memcpy will probably be faster, but it's more likely you will make a mistake using it. It may depend on how smart your optimizing compiler is.

Your code is incorrect though. It should be:

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

Generally speaking, the worst case scenario will be in an un-optimized debug build where memcpy is not inlined and may perform additional sanity/assert checks amounting to a small number of additional instructions vs a for loop.

However memcpy is generally well implemented to leverage things like intrinsics etc, but this will vary with target architecture and compiler. It is unlikely that memcpy will ever be worse than a for-loop implementation.

People often trip over the fact that memcpy sizes in bytes, and they write things like these:

// 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));

You can protect yourself here by using language features that let you do some degree of reflection, that is: do things in terms of the data itself rather than what you know about the data, because in a generic function you generally don't know anything about the data:

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

Note that you don't want the "&" infront of "myGlobalArray" because arrays automatically decay to pointers; you were actually copying "nums" to the address in memory where the pointer to the myGlobalArray[0] was being held.

( Edit note: I'd typo'd int[] nums when I mean't int nums[] but I decided that adding C array-pointer-equivalence chaos helped nobody, so now it's int *nums :) )

Using memcpy on objects can be dangerous, consider:

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));

This is the WRONG way to copy objects that aren't POD (plain old data). Both f1 and f2 now have a std::string that thinks it owns "hello". One of them is going to crash when they destruct, and they both think they own the same vector of integers that contains 42.

The best practice for C++ programmers is to use std::copy :

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

Note per Remy Lebeau or since C++11

std::copy_n(nums, numNums, myGlobalArray);

This can make compile time decisions about what to do, including using memcpy or memmove and potentially using SSE/vector instructions if possible. Another advantage is that if you write this:

struct Foo {
    int m_i;
};

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

and later on change Foo to include a std::string , your code will break. If you instead write:

struct Foo {
    int m_i;
};

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

the compiler will switch your code to do the right thing without any additional work for you, and your code is a little more readable.

For performance, use memcpy (or equivalents). It's highly optimised platform-specific code for shunting lots of data around fast.

For maintainability, consider what you're doing - the for loop may be more readable and easier to understand. (Getting a memcpy wrong is a fast route to a crash or worse)

Essentially, as long as you are dealing with POD types (Plain Ol' Data), such as int, unsigned int, pointers, data-only structs, etc... you are safe to use mem*.

If your array contains objects, use the for loop, as the = operator may be required to ensure proper assignment.

A simple loop is slightly faster for about 10-20 bytes and less (It's a single compare+branch, see OP_T_THRES ), but for larger sizes, memcpy is faster and portable.

Additionally, if the amount of memory you want to copy is constant, you can use memcpy to let the compiler decide what method to use.

Side note: the optimizations that memcpy uses may significantly slow your program down in a multithreaded environment when you're copying a lot of data above the OP_T_THRES size mark since the instructions this invokes are not atomic and the speculative execution and caching behavior for such instructions doesn't behave nicely when multiple threads are accessing the same memory. Easiest solution is to not share memory between threads and only merge the memory at the end. This is good multi-threading practice anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM