memcpy vs for循环 - 从指针复制数组的正确方法是什么?

时间:2011-01-18 21:10:31

标签: c++ arrays pointers copy

我有一个函数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];
    }
}

我缺少第三种选择吗?

6 个答案:

答案 0 :(得分:61)

是的,第三个选项是使用C ++构造:

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

使用任何理智的编译器,它:

  • 在大多数情况下应该是最佳的(在可能的情况下将编译为memcpy()),
  • 是类型安全的,
  • 当您决定将数据类型更改为非基本类型(即它调用复制构造函数等)时,优雅地应对,
  • 当您决定更改为容器类时,优雅地应对。

答案 1 :(得分:22)

Memcpy可能会更快,但你更有可能在使用它时犯了错误。 这可能取决于优化编译器的智能程度。

但您的代码不正确。它应该是:

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

答案 2 :(得分:5)

一般来说,最糟糕的情况是在未优化的调试版本中,其中memcpy未内联,并且可能执行额外的健全/断言检查,相当于少量额外指令与for循环。 / p>

然而,memcpy通常很好地实现了利用内在函数等内容,但这会因目标架构和编译器而异。 <{1}}不太可能比for循环实现更糟糕。

人们经常会对memcpy以字节为单位的大小进行绊倒,他们会写下这样的内容:

memcpy

您可以通过使用可以进行某种程度反思的语言功能来保护自己,即:根据数据本身而不是您对数据的了解来做事,因为在通用功能中,您通常不会&# 39;对数据一无所知:

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

请注意,您并不想要&#34;&amp;&#34;面对&#34; myGlobalArray&#34;因为数组会自动衰减到指针;你实际上正在复制&#34; nums&#34;到内存中指向myGlobalArray [0]的指针的地址。

编辑说明:我的错误&#39; d void foo (int* nums, size_t numNums) { memcpy(myGlobalArray, nums, numNums * sizeof(*nums)); } 我的意思是int[] nums但我决定添加C array-pointer-equivalence混乱帮助没人,现在它int nums[]:)

在对象上使用int *nums可能很危险,请考虑:

memcpy

这是复制不是POD(普通旧数据)的对象的错误方法。 f1和f2现在都有一个std :: string,它认为它拥有&#34;你好&#34;。其中一个在破坏时会崩溃,他们都认为它们拥有包含42个整数的相同向量。

C ++程序员的最佳实践是使用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));

std::copy

注意每个Remy Lebeau 或自C ++ 11以来

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

这可以使编译时决定做什么,包括使用std::copy_n(nums, numNums, myGlobalArray); memcpy并尽可能使用SSE /向量指令。另一个优点是,如果你这样写:

memmove

稍后更改Foo以包含struct Foo { int m_i; }; Foo f1[10], f2[10]; memcpy(&f1, &f2, sizeof(f1)); ,您的代码将会中断。如果您改为写:

std::string

编译器会切换你的代码来做正确的事情而不需要任何额外的工作,你的代码更具可读性。

答案 3 :(得分:1)

为了提高性能,请使用memcpy(或等效物)。它是高度优化的平台特定代码,用于快速分流大量数据。

为了可维护性,请考虑您正在做什么 - for循环可能更易读,更容易理解。 (得到一个记忆错误是通往崩溃或更糟糕的快速途径)

答案 4 :(得分:1)

基本上,只要您处理POD类型(Plain Ol'数据),例如int,unsigned int,指针,仅数据结构等等,您就可以安全地使用mem *。

如果您的数组包含对象,请使用for循环,因为可能需要=运算符以确保正确分配。

答案 5 :(得分:1)

一个简单的循环大约要快10-20个字节,甚至更少(这是一个单行+分支,请参见OP_T_THRES),但是对于较大的循环,memcpy则更快,更可移植。 >

此外,如果要复制的内存量恒定,则可以使用memcpy让编译器决定使用哪种方法。

侧面说明:当您复制大量大于memcpy大小的数据时,OP_T_THRES使用的优化可能会大大降低程序在多线程环境中的运行速度,因为此调用的指令并不当多个线程正在访问同一内存时,此类指令的原子性和推测性执行以及缓存行为会表现不佳。最简单的解决方案是不在线程之间共享内存,而仅在最后合并内存。无论如何,这是一个很好的多线程实践。