原来C语言还可以这样实现“泛型编程”!

在回答标题问题之前,先了解下什么是泛型编程。

泛型编程(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。C++支持泛型编程,也就是模板,比如:

#include 
template <class T>
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中提供的泛型宏也是有限的。

声明:

本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值