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

mjn*_*n12 35 c++ arrays pointers copy

我有一个foo(int[] nums)我理解的功能基本上相当于foo(int* nums).在里面foo我需要将指向的数组的内容复制nums到一些int[10]声明的范围内foo.我理解以下内容无效:

void foo (int[] nums) 
{
    myGlobalArray = *nums
}
Run Code Online (Sandbox Code Playgroud)

复制数组的正确方法是什么?我应该像这样使用memcpy:

void foo (int[] nums)
{
    memcpy(&myGlobalArray, nums, 10);
}
Run Code Online (Sandbox Code Playgroud)

或者我应该使用for循环?

void foo(int[] nums)
{
    for(int i =0; i < 10; i++)
    {
        myGlobalArray[i] = nums[i];
    }
}
Run Code Online (Sandbox Code Playgroud)

我缺少第三种选择吗?

Oli*_*rth 62

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

std::copy(&nums[0], &nums[10], myGlobalArray);
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我似乎在评论中留下了一个重要的词:"可能" (3认同)

Jay*_*Jay 22

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

但是你的代码不正确.它应该是:

memcpy(&myGlobalArray, nums, 10 * sizeof(int) );
Run Code Online (Sandbox Code Playgroud)

  • c 库的作者花费了大量时间对其进行优化。如果你编写代码来做同样的事情,那么这取决于编译器优化你的代码的程度。 (2认同)

kfs*_*one 5

一般来说,最糟糕的情况是在未优化的调试版本中,其中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));
Run Code Online (Sandbox Code Playgroud)

您可以通过使用可以进行某种程度反思的语言功能来保护自己,即:根据数据本身而不是您对数据的了解来做事情,因为在通用函数中,您通常不知道任何事情关于数据:

void foo (int* nums, size_t numNums)
{
    memcpy(myGlobalArray, nums, numNums * sizeof(*nums));
}
Run Code Online (Sandbox Code Playgroud)

请注意,您不希望"&global"面向"myGlobalArray",因为数组会自动衰减为指针; 你实际上是将"nums"复制到内存中指向myGlobalArray [0]的指针的地址.

(编辑注释:int[] nums当我的意思是,我错了,int nums[]但我决定添加C数组 - 指针 - 等效混乱帮助没人,所以现在它是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));
Run Code Online (Sandbox Code Playgroud)

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

C++程序员的最佳实践是使用std::copy:

std::copy(nums, nums + numNums, myGlobalArray);
Run Code Online (Sandbox Code Playgroud)

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

std::copy_n(nums, numNums, myGlobalArray);
Run Code Online (Sandbox Code Playgroud)

这可以使编译时决定做什么,包括使用memcpymemmove可能使用SSE /向量指令.另一个优点是,如果你这样写:

struct Foo {
    int m_i;
};

Foo f1[10], f2[10];
memcpy(&f1, &f2, sizeof(f1));
Run Code Online (Sandbox Code Playgroud)

然后改变Foo包含一个std::string,你的代码就会破解.如果你改为写:

struct Foo {
    int m_i;
};

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

编译器将切换您的代码以做正确的事情而无需任何额外的工作,并且您的代码更具可读性.