C语言实现泛型函数

由于C语言是强类型语言,所以通常我们在编写一些函数的时候就需要指定函数的类型。这就会导致同样的函数行为因为处理的类型不同,就可能需要为不同的类型编写不同的函数版本。

比如用于交换两个变量值的如swap函数:

1
2
3
4
5
void swap(int *v1, int *v2) {
int *temp = v1;
*v1 = *v2;
*v2 = *temp;
}

上面的swap函数只能交换两个int类型的整数。

如果需要交换两个double类型的浮点数就需要再写一个double类型版本的swap函数:

1
2
3
4
5
void swap(double *v1, double *v2) {
int *temp = v1;
*v1 = *v2;
*v2 = *temp;
}

所谓泛型swap函数就是一个swap函数就能够处理不同类型的交换工作。在C++有模版来实现泛型,但是C语言中没有模板,所以我们可以使用void类型的指针来实现泛型swap函数,任何类型的指针都可以赋值给void类型的指针。

泛型swap函数实现:

1
2
3
4
5
6
7
8
void swap(void *v1, void *v2, size_t size) {
void *temp = malloc(size);
assert(temp != NULL);
memcpy(temp, v1, size);
memcpy(v1, v2, size);
memcpy(v2, temp, size);
free(temp);
}

先对函数中出现的3个函数分别加以说明:

  • malloc

    函数原型为void *malloc(size_t __size)。该函数用来向堆中动态申请一块内存。它会向堆中动态申请参数__size个字节大小的内存空间,如果申请成功就返回申请到的空间首地址,申请失败就返回NULL

  • memcpy

    函数原型为void *memcpy(void *dest, const void *src, size_t n)。该函数用来实现内存拷贝,它把参数src所指向的内存空间拷贝n个字节到参数dest所指向的内存空间中。

  • free

    函数原型为void free(void *ptr),该函数用来释放动态申请的内存。它把参数ptr所指向的动态申请的内存空间进行释放。

函数签名void swap(void *v1, void *v2, size_t size) 中的前两个参数使用了void类型的指针,第三个参数size表示需要交换的类型在内存中所占字节数量。
第2~3行代码是向堆中申请size个字节大小的空间,然后将空间首地址赋值给temp指针,并断言内存是否申请成功。
第4~6行代码是通过内存拷贝的方式来实现交换步骤。
第7行代码是把在第3行代码中动态申请到的size个字节大小的内存空间进行释放,以免发生内存泄漏。

swap函数进行调用,交换两个int类型变量的值:

1
2
3
4
5
6
7
8
int main() {
int a = 123;
int b = 321;
printf("before swap: %d, %d\n", a, b);
swap(&a, &b, sizeof(int));
printf("after swap: %d, %d\n", a, b);
return 0;
}

输出:

1
2
before swap: 123, 321
after swap: 321, 123

swap函数进行调用,交换两个double类型变量的值:

1
2
3
4
5
6
7
8
int main() {
double a = 123;
double b = 321;
printf("before swap: %f, %f\n", a, b);
swap(&a, &b, sizeof(double));
printf("after swap: %f, %f\n", a, b);
return 0;
}

输出:

1
2
before swap: 123.000000, 321.000000
after swap: 321.000000, 123.000000

运行实现中的完整代码还需要引入头文件:

1
2
3
4
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

以上就是泛型swap函数的具体实现,主要是运用了void指针和内存拷贝。可以借鉴实现思路来研究关于C语言实现泛型的更多例子。