Menu
CoddyTech

泛型交换

本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 51 课,共 61 课。

编程中一个常见的操作是交换两个值。通常,你需要为每种类型分别编写一个交换函数:一个用于 int,另一个用于 double,依此类推。但借助 void* 和 memcpy,我们可以编写一个函数来交换任意两个变量。

关键在于,交换只是移动字节。如果你知道数据的大小,就可以在不知道具体类型的情况下复制字节。函数签名如下:

void generic_swap(void* a, void* b, size_t size);

size 参数告诉函数要移动多少字节。在函数内部,我们使用一个临时缓冲区和来自 <string.h> 的 memcpy 来交换这些字节:

void generic_swap(void* a, void* b, size_t size) {
    char temp[size];      // 临时缓冲区
    memcpy(temp, a, size);  // temp = a
    memcpy(a, b, size);     // a = b
    memcpy(b, temp, size);  // b = temp
}

现在,这个函数适用于任意类型:

int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x 现在是 10,y 现在是 5
double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// 现在 p 的值是 2.71,q 的值是 3.14

这种使用 void* 和 size_t 的模式,是 C 标准库实现 qsort 和 bsearch 等通用函数的方式。

challenge icon

挑战

简单

让我们来构建一个通用交换工具,无论变量的类型是什么,都能交换任意两个变量的值:这要借助 void* 指针和 memcpy 的强大功能。

你要将代码分布在 3 个文件中:

  • swap.h:声明 generic_swap 函数,该函数接受两个 void* 指针和一个 size_t 参数,用于指定要交换的数据大小。别忘了添加头文件保护!
  • swap.c:实现 generic_swap 函数。使用临时缓冲区和 memcpy 在两个内存位置之间移动字节。记住交换的 3 个步骤:先将第一个值复制到临时缓冲区,再将第二个值复制到第一个位置,最后将临时缓冲区中的值复制到第二个位置。
  • main.c:演示如何使用通用交换函数处理不同的数据类型。读取一个类型标识符(i 表示整数,d 表示双精度浮点数),然后读取两个值。创建两个对应类型的变量,使用 generic_swap 函数交换它们,然后打印交换后的两个值。

程序会接收 3 个输入:

  1. 类型标识符:i 表示整数,d 表示双精度浮点数
  2. 第一个值
  3. 第二个值

交换后,将两个值分别打印在单独的行上。对于双精度浮点数,保留 2 位小数。

输入为 i、5 和 10 时的输出示例:

10
5

输入为 d、3.14 和 2.71 时的输出示例:

2.71
3.14

输入为 i、-7 和 42 时的输出示例:

42
-7

输入为 d、99.99 和 0.01 时的输出示例:

0.01
99.99

这种方法的优点是,generic_swap 函数完全不知道自己交换的是什么类型。它只移动字节。同一个函数适用于整数、双精度浮点数、结构体或任何其他数据类型。你需要引入 <string.h> 来使用 memcpy,并引入 <stddef.h> 或 <stdlib.h> 来使用 size_t。

亲手试试

#include <stdio.h>
#include "swap.h"

int main() {
    char type;
    scanf("%c", &type);
    
    if (type == 'i') {
        int a, b;
        scanf("%d", &a);
        scanf("%d", &b);
        
        // TODO:调用 generic_swap 交换 a 和 b 的值
        // 提示:使用 &a、&b 和 sizeof(int)
        
        printf("%d\n", a);
        printf("%d\n", b);
    } else if (type == 'd') {
        double a, b;
        scanf("%lf", &a);
        scanf("%lf", &b);
        
        // TODO:调用 generic_swap 交换 a 和 b 的值
        // 提示:使用 &a、&b 和 sizeof(double)
        
        printf("%.2f\n", a);
        printf("%.2f\n", b);
    }
    
    return 0;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C 在线编译器