C语言实现泛型 C11关键字_Generic

  在 C11 标准中,_Generic 关键字让 C 语言如同 C++ 等面向对象程序设计语言一样,支持轻量级的泛型编程设计。

1 泛型是什么

  泛型编程(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。
  C++通过模板来支持泛型编程,比如下面这段代码,在定义add()函数时候并没有明确指出其返回类型、参数类型。

#include <iostream>
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;
}

2 C语言的泛型

  C语言本身不支持真正意义上的泛型编程,但是却在一定程度上可以“实现泛型编程”,即C11标准的 _Generic 关键字(C++不支持该关键字)。

3 _Generic 关键字

   _Generic 关键字提供了一种在 编译时 根据 赋值表达式 的类型在 泛型关联表 中选择一个表达式的方法,因此可以将一组不同类型却功能相同的函数抽象为一个统一的接口,以此来实现泛型,语法形式如下:

_Generic ( assignment-expression , generic-assoc-list )

assignment-expression:赋值表达式,可以认为是变量var
generic-assoc-list:泛型关联表,其语法为:

type-name : expression, type-name : expression, ..., default : expression

  是不是看起来很懵,换成人话就是下面的格式:

_Generic((var), type1 : ..., type2 : ..., ……, default : ...) 

  这里需要注意两点:

  1. default不是必须的
  2. generic-assoc-list中的expression仅仅是普通的宏替换

4 泛型实例1

  举个例子,要想实现getTypeName(var)函数,该函数的作用是返回变量var的类型,可以这样写:

#define GET_TYPENAME(var) _Generic((var),\
    int:"int", \
    char:"char", \
    float:"float", \
    double:"double", \
    char *:"char *", \
    default:"other type")
int main(int argc, char const *argv[])
{
    int x;
    int* x1;
    char s[10];
    printf("type: x = %s\n",GET_TYPENAME(x));
    printf("type: x1 = %s\n",GET_TYPENAME(x1));
    printf("type: s = %s\n",GET_TYPENAME(s));
    return 0;
}

  测试一下,运行结果:

type: x = int
type: x1 = other type
type: s = char *

5 泛型实例2

  现在我们来实现最开始c++模板的add()函数。由于c语言不支持重载,所以我们要先定义两个不同类型的add()函数,再去使用 _Generic

int add_int(int a,int b)
{
    printf("%d + %d = %d\n",a,b,a+b);
    return a+b;
}
float add_float(float a,float b)
{
    printf("%f + %f = %f\n",a,b,a+b);
    return a+b;
}
void unsupport()
{
    printf("unsupport type\n");
}
#define ADD(x,y) _Generic((x),\
    int:add_int(x,y),\
    float:add_float(x,y),\
    default:unsupport())
int main(int argc, char const *argv[])
{
    ADD(1,2);
    ADD(1.1f,2.2f);
    return 0;
}

运行结果:

1 + 2 = 3
1.100000 + 2.200000 = 3.300000

  我们可以注意到此时调用者并不需要区分两个被加数的类型就可以统一调用ADD。不过这里有一个确定就是只判断了一个变量x的类型,如果两个变量x,y的类型不一样时仍然会会出现错误。比如下面这种情况:

    ADD(1,1.1);
运行结果:
  	1 + 1 = 2

  其次,由于 "generic-assoc-list中的expression仅仅是普通的宏替换“ ,_Generic语句中的参数也可以放在后面,也可以写成下面这种(这句没写default):

#define ADD1(x,y) _Generic((x),int:add_int,float:add_float)(x,y)

5 泛型中的值不会发生改变

  继续上面的GET_TYPENAME代码,

int main(void)
{
    int x = 1;
    GET_TYPENAME(x++);
    GET_TYPENAME(++x);
    GET_TYPENAME(x*=2);
    GET_TYPENAME(x = 10)
    printf("x = %d\n",x);
}

运行结果:

x = 1

  可见, 在泛型_Generic的语句中,++ 、*=、 = 等操作都不会让变量发生改变

  来看另一段代码,来自百度百科对C11中_Generic关键字的例程。

int main(void)
{
    int a=10;
    int b=0,c=0;
    _Generic(a+0.1f,int:b,float:c,default:b)++;
    printf("b=%d,c=%d\n",b,c);
 
    _Generic(a+=1.1f,int:b,float:c,default:b)++;
    printf("a=%d,b=%d,c=%d\n",a,b,c);
}

运行结果:

b=0,c=1
a=10,b=1,c=1

解释:

  1. _Generic(a+0.1f,int:b,float:c,default:b)++;

a为int,a + 0.1f会被转换成float,所以_Generic(泛型)发现传入的是float,所以执行float的内容,整个_Generic宏替换为c,最终变成c++,printf格式化输出b=0,c=1

  1. _Generic(a+=1.1f,int:b,float:c,default:b)++;

a为int,a+=1.1f执行了+=操作并不会改变a的值,执行完后_Generic(泛型)才进行判断a的类型,因为a的类型为int,所以执行int的操作,最终变成b++,printf格式化输出a=10,b=1,c=1

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
c语言是一种面向过程的编程语言,不像某些面向对象的语言那样内置了泛型的概念,但我们可以通过一些技巧来实现搜索泛型代码。 一种常见的方法是使用void指针作为参数,这样可以接收任意类型的数据。我们可以将待搜索的元素转换为void指针,并通过回调函数比较元素是否满足搜索条件。以下是一个简单的示例: ```c #include <stdio.h> typedef struct { int id; char name[20]; } Student; int compareInt(const void* a, const void* b) { int* pa = (int*)a; int* pb = (int*)b; return *pa - *pb; } int compareString(const void* a, const void* b) { char** pa = (char**)a; char** pb = (char**)b; return strcmp(*pa, *pb); } int compareStudent(const void* a, const void* b) { Student* sa = (Student*)a; Student* sb = (Student*)b; return sa->id - sb->id; } void* search(const void* array, int size, const void* target, int (*compare)(const void*, const void*)) { for(int i = 0; i < size; i++) { const void* current = (char*)array + i * size; // 根据实际类型大小计算当前元素的指针 if(compare(current, target) == 0) { return (void*)current; } } return NULL; } int main() { int intArray[] = {1, 2, 3, 4, 5}; int intTarget = 3; int* intResult = (int*)search(intArray, sizeof(int), &intTarget, compareInt); printf("Int result: %d\n", *intResult); char* stringArray[] = {"apple", "banana", "cherry", "grape"}; char* stringTarget = "cherry"; char** stringResult = (char**)search(stringArray, sizeof(char*), &stringTarget, compareString); printf("String result: %s\n", *stringResult); Student studentArray[] = {{1, "Tom"}, {2, "Jerry"}, {3, "Alice"}}; Student studentTarget = {2, "Jerry"}; Student* studentResult = (Student*)search(studentArray, sizeof(Student), &studentTarget, compareStudent); printf("Student result: id=%d, name=%s\n", studentResult->id, studentResult->name); return 0; } ``` 在搜索函数中,我们通过计算偏移量来获取当前元素的指针,然后通过调用回调函数来比较元素。如果找到满足条件的元素,就返回该元素的地址,否则返回NULL。 这样我们就可以通过search函数来搜索不同类型的数组和元素了。当我们需要搜索其他类型的数据时,只需编写相应的compare函数即可。 这只是一个简单的示例,实际上,泛型代码的实现可以更加复杂和灵活。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值