memcpy vs for loop - What's the proper way to copy an array from a pointer?
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 (i.e. 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.