泛型交换
本课属于 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 现在是 5double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// 现在 p 的值是 2.71,q 的值是 3.14这种使用 void* 和 size_t 的模式,是 C 标准库实现 qsort 和 bsearch 等通用函数的方式。
挑战
简单让我们来构建一个通用交换工具,无论变量的类型是什么,都能交换任意两个变量的值:这要借助 void* 指针和 memcpy 的强大功能。
你要将代码分布在 3 个文件中:
swap.h:声明generic_swap函数,该函数接受两个void*指针和一个size_t参数,用于指定要交换的数据大小。别忘了添加头文件保护!swap.c:实现generic_swap函数。使用临时缓冲区和memcpy在两个内存位置之间移动字节。记住交换的 3 个步骤:先将第一个值复制到临时缓冲区,再将第二个值复制到第一个位置,最后将临时缓冲区中的值复制到第二个位置。main.c:演示如何使用通用交换函数处理不同的数据类型。读取一个类型标识符(i表示整数,d表示双精度浮点数),然后读取两个值。创建两个对应类型的变量,使用generic_swap函数交换它们,然后打印交换后的两个值。
程序会接收 3 个输入:
- 类型标识符:
i表示整数,d表示双精度浮点数 - 第一个值
- 第二个值
交换后,将两个值分别打印在单独的行上。对于双精度浮点数,保留 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;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C 在线编译器