这篇文章给大家介绍C语言中怎么实现泛型编程,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:申请域名、虚拟主机、营销软件、网站建设、福田网站维护、网站推广。
泛型编程(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。C++支持泛型编程,也就是模板,比如:
// 来源:公众号【 编程珠玑】 // 作者:守望先生 #includetemplate T add(T a,T b){ T ret = a + b; std::cout<< a << " + " << b <<" = " << ret << std::endl; return ret; } int main(){ add(1,2); // 整数相加 add(1.2,2.3); // 浮点数相加 return 0; }
运行结果:
1 + 2 = 3 1.2 + 2.3 = 3.5
从上面的结果可以看到,对于调用add函数,如果传入的是整型,则按照整型加法计算,如果是浮点数,则按照浮点数进行加法计算。也就是说,add函数没有针对特定类型(泛型)。
你同样可以使用重载实现上面的功能,但是存在大量重复代码。
C语言支持泛型编程吗?
很遗憾,C语言本身不支持真正意义上的泛型编程,但是却在一定程度上可以“实现泛型编程”。
_Generic关键字
_Generic是C11的关键字,通过该关键字可以有一个泛型表达式:
_Generic((value). int:"int", float:"float",char*:"char*",default:"other type")
什么意思呢?如果value是int类型,那么表达式的值就是“int”,其他的以此类推。看起来是不是和switch语句有点类似呢?
根据这个示例,我们来实现一个功能,打印变量或常量到底是什么类型:
// 来源:公众号【编程珠玑】 // 作者:守望先生 #include#define TYPE(v) _Generic((v), \ int:"int", \ char:"char", \ float:"float", \ double:"double", \ char*:"char*", \ default:"other type") int main(void) { printf("1 + 2 type: %s\n",TYPE(1 + 2)); printf("1/3 type: %s\n",TYPE(1/3)); printf("2/3 type: %s\n",TYPE((float)2/3)); printf("xxx type: %s\n",TYPE("xxx")); return 0; }
这里为了方便使用,我们通过define关键字,将泛型表达式简化。
运行结果:
1 + 2 type: int 1/3 type: int 2/3 type: float xxx type: char*
可以看到通过TYPE就可以获得表达式的结果类型,这对初学者来说,可真是福音了。
泛型算法
既然C语言有_Generic关键字了,那么我们尝试实现开头C++示例代码中的加法。看过上面的例子后,相信你已经会了:
// 来源:公众号【编程珠玑】 // 作者:守望先生 #include// int类型加法 int addI(int a, int b) { printf("%d + %d = %d\n",a,b, a + b ); return (a + b); } // double类型加法 double addF(double a, double b) { printf("%f + %f = %f\n",a,b, a + b ); return (a + b); } void unsupport(int a,int b) { printf("unsupport type\n"); } #define ADD(a,b) _Generic((a), \ int:addI(a,b),\ double:addF(a,b), \ default:unsupport(a,b)) int main(void) { ADD(1 , 2); ADD(1.1,2.2); return 0; }
观察上面的代码,我们注意到:
在这里,我们需要定义两种类型的加法(实际上,通过C++的模板,由编译器帮我们完成了这件事),由于C语言中并不支持重载,因此两个加法的函数名不一样。
由于涉及参数有两个,在做类型判断时,如果两个参数不一致,可能仍然存在编译问题
调用者无需区分被加对象是什么类型,都可以统一使用ADD
C99的tgmath.h
前面说到,_Generic关键字在C11中才有,那么C99怎么办呢?实际上,tgmath.h中提供了一些泛型类型宏,如果math.h的函数中定义了float,double和long double版本,tgmath就会提供一个泛型类型宏。效果和前面的例子一样,举个例子:
// 来源:公众号【编程珠玑】 // 作者:守望先生 #include#include int main(void) { float f = 4.0f; long double d = 1.44; printf("%f\n",sqrt(f)); // 实际上调用了sqrtf printf("%Lf\n",sqrt(d)); // 实际上调用了sqrtl return 0; }
编译运行结果:
2.000000 1.200000
但是不得不说,tgmath中提供的泛型宏也是有限的。
void *指针
众所周知,C语言中void *指针是一种无类型指针,从这点看,也可以算是泛型指针了。而它的使用在C语言中是非常常见的,举例来说,在《高级指针话题-函数指针》中,我们介绍了快速排序接口的使用,它的函数声明是这样的:
#includevoid qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
库函数qsort实际上就是泛型排序算法了,它可以针对任何类型的数据进行排序。当然有一个前提,就是你需要按照它的协议,实现一个compar函数,用于比较大小。
关于C语言中怎么实现泛型编程就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。