一般来说,最坏的情况是在未优化的调试版本中,memcpy 没有内联,并且可能会执行额外的健全性/断言检查,相对于 for 循环而言,需要少量额外的指令。
但是memcpy 通常可以很好地实现以利用内在函数等,但这会因目标体系结构和编译器而异。 memcpy 不太可能比 for 循环实现更糟糕。
人们经常对 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));
您可以在这里通过使用允许您进行某种程度反思的语言功能来保护自己,即:根据数据本身而不是您对数据的了解来做事,因为在通用函数中您通常不会对数据一无所知:
void foo (int* nums, size_t numNums)
{
memcpy(myGlobalArray, nums, numNums * sizeof(*nums));
}
请注意,您不希望“myGlobalArray”前面的“&”,因为数组会自动衰减为指针;您实际上是在将“nums”复制到内存中指向 myGlobalArray[0] 的指针所在的地址。
(编辑说明:当我的意思不是int nums[] 时,我会打错int[] nums,但我决定添加C array-pointer-equivalence 混乱对任何人都没有帮助,所以现在是int *nums :))
在对象上使用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 的整数向量。
C++ 程序员的最佳实践是使用std::copy:
std::copy(nums, nums + numNums, myGlobalArray);
Remy Lebeau 的注释 或自 C++11 起
std::copy_n(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);
编译器将切换您的代码以执行正确的操作,而无需为您做任何额外的工作,并且您的代码更具可读性。