5. 数组的复制

这一节我们来讲解数组的复制。

先说结论,数组不允许直接使用 赋值表达式 直接复制。

我们先看下面这段代码。

示例程序

// filename: array_copy.c
#include <stdio.h>

int main(int argc, char *argv[]) {
    int arr1[3] = {100, 200, 300};
    int arr2[3];

    arr2 = arr1;  // 报错,数组不允许直接复制。

    return 0;
}

编辑时报错,结果如下:

weimingze@mzstudio:~$ gcc -o array_copy array_copy.c
temp.c: In function ‘main’:
temp.c:7:10: error: assignment to expression with array type
    7 |     arr2 = arr1;  // 报错,数组不允许直接复制。
      |          ^

可见数组使用赋值表达式是不符合语法规则的。不能使用赋值表达式直接复制。

其实要实现数组的复制就是要让两个数组占用的内存的内容进行完整的复制。如下图所示:

  arr1      arr1[0]             arr1[1]             arr1[2]
----+----+----+----+----+----+----+----+----+----+----+----+----+----
... |        100        |        200        |        300        | ...
----+----+----+----+----+----+----+----+----+----+----+----+----+----
              |                   |                   |
              |                   |                   |
  arr2        V                   V                   V
----+----+----+----+----+----+----+----+----+----+----+----+----+----
... |                   |                   |                   | ...
----+----+----+----+----+----+----+----+----+----+----+----+----+----

关于数组的复制有如下两种常用的方法:

  1. 使用 memcpy 直接对两段内存的内容进行复制。
  2. 使用循环对数组内的数据元素依次复制。

示例1

使用 memcpy 实现数组的复制;

// filename: array_copy_by_memcpy.c
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    int arr1[3] = {100, 200, 300};
    int arr2[3];

    // arr2 = arr1;  // 报错,数组不允许直接复制。
    memcpy(&arr2, &arr1, sizeof(arr1));

    // 打印复制后的结果
    for (int i = 0; i < sizeof(arr2)/sizeof(arr2[0]); i++) {
        printf("arr2[%d]: %d\n", i, arr2[i]);
    }
    return 0;
}

运行结果如下

weimingze@mzstudio:~$ gcc -o array_copy_by_memcpy array_copy_by_memcpy.c
weimingze@mzstudio:~$ ./array_copy_by_memcpy
arr2[0]: 100
arr2[1]: 200
arr2[2]: 300

关于上述程序的解释:

  1. memcpy(&arr2, &arr1, sizeof(arr1)); 也可以写成 memcpy(arr2, arr1, sizeof(arr1));,即数组取地址&arr2 和 直接使用数组的返回值 arr2 都会返回数组的起始地址,地址的值相同,只是返回的指针的数据类型不同(下一节我们在详细解释)。
  2. sizeof(arr2)/sizeof(arr2[0]) 是计算数组的元素个数。即:整个数组占用内存的字节数 除以 第一个数据元素占用的字节数。这样做的好处是当数组长度在声明时发生改变,则这段代码的返回值会动态计算,从而减少代码的改动。

示例2

使用循环对数组内的数据元素依次复制,实现数组的复制;

// filename: array_copy_by_loop.c
#include <stdio.h>

int main(int argc, char *argv[]) {
    int arr1[3] = {100, 200, 300};
    int arr2[3];

    // arr2 = arr1;  // 报错,数组不允许直接复制。
    for (int i = 0; i < sizeof(arr2)/sizeof(arr2[0]); i++) {
        arr2[i] = arr1[i]; // 依次复制数据元素。
    }

    // 打印复制后的结果
    for (int i = 0; i < sizeof(arr2)/sizeof(arr2[0]); i++) {
        printf("arr2[%d]: %d\n", i, arr2[i]);
    }
    return 0;
}

运行结果如下

weimingze@mzstudio:~$ gcc -o array_copy_by_loop array_copy_by_loop.c
weimingze@mzstudio:~$ ./array_copy_by_loop
arr2[0]: 100
arr2[1]: 200
arr2[2]: 300

实验:

写代码,分别用两种方法实现对 double 类型的数组的复制。