在程序设计中,泛型编程指那些没有特定类型,但是一旦指定一种类型,就可 以转换成指定类型的代码。例如,C++在模板中可以创建泛型算法,然后编译器根据指定的类型自动使用实例化代码。C没有这种功能。然而,C11新增了一种表达式,叫作泛型选择表达式
, 可根据表达式的类型(即表达式的类型是int 、double 还是其他类型)选择一个值。泛型选择表达式不是预处理器指令,但是在一些泛型编程中它常用作#define宏定义的一部分。
_Generic是C11新增的关键字,_Generic表达式的定义如下:
_Generic(X, type: return value, ...,default: return value)
_Generic表达式的第一个参数是需要进行判断的值,表达式会使用该值与后面的type依次进行匹配,如果找到了匹配的type,返回type后面的return value。如果没有找打匹配的类型,返回default后面的值。
值的注意的是:返回值可以是函数指针
,也就是我们可以通过x的类型判断调用不同的处理方法,这样我们就能实现类似C++泛型的方法。
下面,我们给出一个方法,这个方法用于打印不同数据类型在内存中的二进制值。我们在分析数据转换或者验证不同数据类型编码规则的时候这个方法很有用。
首先,我们定义一个类函数宏
#define PRINTBIT(X) _Generic((X),\
char:printBit_c,\
unsigned char:printBit_c,\
short:printBit_s,\
unsigned short:printBit_s,\
int:printBit_i,\
unsigned int:printBit_i,\
long:printBit_l,\
unsigned long:printBit_l,\
long long:printBit_ll,\
unsigned long long:printBit_ll,\
float:printBit_f,\
double:printBit_d,\
default:printBit_ll)(X)
可以看到,函数基本列出了所有的基本类型,根据不同的基本类型使用不同方法,注意最后的(X)
,这一步是函数的实际调用,我们的泛型表达式返回值是一个函数指针,通过调用函数来执行相应的方法,下面,给出方法的实现:
/**
* 打印char/unsigned char类型的内存数据
*/
extern void printBit_c(char val);
/**
* 打印short/unsigned short类型的内存数据
*/
extern void printBit_s(short val);
/**
* 打印int/unsigned int类型的内存数据
*/
extern void printBit_i(int val);
/**
* 打印long/unsigned long类型的内存数据
*/
extern void printBit_l(long val);
/**
* 打印long long/unsigned long long类型的内存数据
*/
extern void printBit_ll(long long val);
/**
* 打印float类型的内存数据
*/
extern void printBit_f(float val);
/**
* 打印double类型的内存数据
*/
extern void printBit_d(double val);
// 如果放开就采用大端法打印二进制数据
//#define BigEndian
void printBit(char* start,int size);
void printBit(char* start,int size)
{
char* temp = start;
#ifdef BigEndian
for(int i=0;i<size;i++)
#else
for(int i=size-1;i>=0;i--)
#endif
{
temp=start+i;
for(int j=7;j>=0;j--)
{
printf("%d",((1<<j)&(*temp))==0?0:1);
}
}
printf("\n");
}
void printBit_c(char val)
{
for(int i=sizeof(val)*8-1;i>=0;i--)
{
printf("%d",((1<<i)&val)==0?0:1);
}
printf("\n");
}
void printBit_s(short val)
{
printBit((char*)&val,sizeof(short));
}
void printBit_i(int val)
{
printBit((char*)&val,sizeof(int));
}
void printBit_f(float val)
{
printBit((char*)&val,sizeof(float));
}
void printBit_d(double val)
{
printBit((char*)&val,sizeof(double));
}
void printBit_l(long val)
{
printBit((char*)&val,sizeof(long));
}
void printBit_ll(long long val)
{
printBit((char*)&val,sizeof(long long));
}
这样,我们只需要使用PRINTBIT(X)
就可以自动识别数据类型,并打印数据,下面是一个测试的例子:
int main(int argc, const char * argv[])
{
unsigned int b = 6550000000;
PRINTBIT(b);
printf("%u\n",b);
float f =b;
PRINTBIT(f);
printf("%f\n",f);
double d =b;
PRINTBIT(d);
printf("%lf\n",d);
return 0;
}
打印结果如下:
10000110011010010001000110000000
2255032704
01001111000001100110100100010010
2255032832.000000
0100000111100000110011010010001000110000000000000000000000000000
2255032704.000000