4. 变量复制和memcpy

这一节我们来讲解变量的赋值和复制。本节我们将要理解赋值运算符的原理,以及 memcpy 函数的原理和用法。同时也为理解下一节《数组的复制》做准备。

我们先来了解赋值表达式的执行过程和运行结果。先看下面这段代码。

char 类型的两个变量的复制

char ch1 = 'A';
char ch2;

ch2 = ch1;
printf("ch1:%c, ch2:%c\n", ch1, ch2);

从上面的代码可以看出,其实相同类型的变量的复制的实质是将 ch1 变量所占内存的一个字节的值复制到了 ch2 变量所占用的一个字节的值。如下图所示:

     ch1  ch2
----+----+----+----
... |'A' |    | ...
----+----+----+----
      |     ^
      |     |
      `-->--`

同样,我们再来看一下 int 类型的两个变量的复制

int i1 = 100;
int i2;

i2 = i1;
printf("i1:%d, i2:%d\n", i1, i2);

在 C 语言中,整数类型的赋值语句实质是将整数(如:i1)占用的四个字节的内容,一次性地复制到另外一个变量(如:i2)的四个字节中。这就形成了如下的结果。

             i1                  i2
----+----+----+----+----+----+----+----+----+----
... |        100        |                   | ...
----+----+----+----+----+----+----+----+----+----
     \_________________/ \_________________/
              |                   ^
              |                   |
              `--------->---------`

其实我们还有别的办法来实现内存的复制,同样可以实现整数赋值的效果,那就是 C 语言的标准库函数 memcpymemcpy 函数可以将开始地址的 n 个字节复制到另外一个内存地址的 n 个字节中。

memcpy 函数的声明定义在头文件 string.h 中。在使用前需要使用预处理指令 #include <string.h> 将头文件 string.h 的函数声明加入到当前程序中。

memcpy 函数的调用格式

// 头文件 string.h
void * memcpy(void * dst, const void * src, size_t n);

参数说明:

  1. 参数 dst 是目标内存地址。
  2. 参数 src 是源内存地址。
  3. n 是需要复制的字节数。

返回值:

示例:

使用 memcpy 实现整数变量的复制来代替 赋值语句。

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

int main(int argc, char *argv[]) {
    int i1 = 100;
    int i2;

    // i2 = i1;  取消变量赋值,使用 memcpy 代替。
    memcpy(&i2, &i1, sizeof(i1));
    printf("i1:%d, i2:%d\n", i1, i2);

    return 0;
}

运行结果如下:

weimingze@mzstudio:~$ gcc -o int_assignment int_assignment.c
weimingze@mzstudio:~$ ./int_assignment
i1:100, i2:100

从上面的例子可以看出,赋值运算符其实就是编译器内部实现的内存复制,不同类型的变量的赋值编译器内部会调用不同的方式的内存复制来实现赋值。

实验:

  1. 使用 memcpy 函数实现不同 double 类型的两个变量的赋值。
  2. 思考:能否使用 memcpy 函数实现两个不同类型的变量的赋值,如 double 和 float 类型变量的赋值?