C语言中比较大小的函数模板,C语言中实现模板函数小结 : 不敢流泪

—-by boluor 2009/5/20

如果要写个函数支持多种数据类型,首先想到的就是C++的模板了,但是有时候只能用C语言,比如在linux内核开发中,为了减少代码量,或者是某面试官的要求…

考虑了一阵子后,就想到了qsort上.qsort的函数原型:

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

快排时,只要自己实现相应数据类型的比较函数cmpare就可以了.如果比较int型时,一个典型的compare函数如下:

int cmp(const void *a,const void *b){

return *((int *)a)-*((int *)b);

}

那么,就是说可以利用void *. void *意指未指定类型,也可以理解为任意类型。其他类型的指针可以直接赋值给void

*变量,但是void *变量需要强制类型转换为其它指针类型。这个相信大家都知道。那么下面以一个简单的题目为例,来探讨如何在C语言中实现模板函数。

方法1: 利用void *.

在看下面的源程序之前,需要了解几点。首先,在32位平台上,任何类型的指针所占的字节都是4个字节,因为32位机器虚拟内存一般为4G,即2的

32次方,只要32位即4个字节就可以足够寻址,sizeof(void *)=4;

其次,虽然各种不同类型的指针所占的空间都为4个字节,但是不同类型的指针所指的空间的字节数却不同(这一点尤为重要,下面的程序我在开始没有调通就因为

这点意识不强)。所以,如果你将一个指针强制转换为另一个类型的指针,指针本身所占的字节是不变的,但是,如果对这个指针进行运算,比如

*p,p++,p-=1等一般都是不同的。 再次,函数指针应该了解下,这里不多说。

最后,因为Sandy跟我说,C++开始的时候模板的实现其实就是利用宏替换,在编译的时候确定类型。所以,为了方便,类型也用了预编译指

令#define。

#include "stdio.h"

#include "stdlib.h"

//typedef int T; //或者下面的也可以.

#define T int

int cmp(const void *a,const void *b){

return *((T *)a)-*((T *)b);

}

/*

//这个FindMin是Sandy写的.felix021也写了个,差不多的就不贴出来的.

void FindMin(const void *arr,int arr_size,int arrmembersize,int *index,

int (*cmp)(const void *,const void *b)){

int i;

*index=0;

char *p=(char *)arr;

char *tmp=p;

for (i=1;i

if (cmp(tmp,p)>0){

tmp=p;

}

p+=arrmembersize;

}

(*index)=((int)(tmp-arr))/arrmembersize;

}

*/

int FindMin(const void *arr,int arr_size,int arrmembersize,int (*cmp)(const void *,const void *)){

char *p=(char *)arr;//可以把指针看作是char *,如果转换为int *,那下面的位移就不正确了.

int index=0;

int i;

for (i=1;i

if (cmp(p+index*arrmembersize,p+i*arrmembersize)>0){

index=i;

}

}

return index;

}

int main(){

int arr[]={2,1,1,2,3,4,5,0,2,3,1,3};

//int *result;

int result;//result保存的是最小值索引.

result=FindMin(arr,12,sizeof(arr[0]),cmp);

printf("%d,%d

",result,arr[result]);

system("PAUSE");

return 0;

}

方法2:利用宏。在编译的时候确定类型。查阅资料的时候,很多都说这种方法比较好,方便调试,也很直观,虽然很啰嗦。

#include

#ifndef _INT_

#define _INT_

#endif

int cmp(const void *a,const void *b){

#ifdef _INT_

return (*(int *)a-*(int *)b);

#elif _FLOAT_

return (fabs(*(float *)a-*(float *)b)<1e-6)?-1:1;

#elif _DOUBLE_

return (fabs(*(double *a)-*(double *)b)<1e-9)?-1:1;

#endif

}

#ifdef _INT_

void FindMin(int *arr,int arr_size,int *result,int cmp(const void *a,const void *b))

#elif _FLOAT_

void FindMin(float *arr,int arr_size,float *result,int cmp(const void *a,const void *b))

#elif _DOUBLE_

void FindMin(double *arr,int arr_size,double *result,int cmp(const void *a,const void *b))

#endif

{

int i;

*result=arr[0];

for(i=1;i

if(cmp(&arr[i],result)>0)

*result=arr[i];

}

}

int main(){

int arr1[]={1,2,4,2,1,7};

int result;

FindMin(arr1,6,&result,cmp);

printf("%d

",result);

return 0;

}

方法3:在findmin中,不用强制类型转换为char *,直接利用memcpy内存拷贝过去,这时,还可以在参数列表中保存结果,而不是索引。此方法由CSDN上的ltc_mouse提供。

#include "stdio.h"

#include "stdlib.h"

#include "string.h"

void FindMin(void *arr,int arr_size,int arrmembersize,void * result,

int (*fpCmp)(const void *,const void *b)){

int i;

unsigned char *pSrc = (unsigned char *)arr;

unsigned char *pRes = (unsigned char *)result;

memcpy( (void *)pRes, (const void *)pSrc, arrmembersize );

for (i=1;i

pSrc += arrmembersize;

if ( fpCmp((const void *)pSrc, result)<0 ){

memcpy( (void *)pRes, (const void *)pSrc, arrmembersize );

}

}

}

int cmp_int(const void *a, const void *b)

{

return ( *(int *)a - *(int *)b );

}

int cmp_double(const void *a, const void *b)

{

return ( (fabs(*(double *)a < *(double *)b ))<1e-9) ? -1 : 1; //这个可能要调整下

}

int main()

{

int iArr[]={1,3,5,-1,3,4,7};

double fArr[]={-3.2, 2.3, 7.8, -9.3, 4.7, 10.5};

int iMin;

double fMin;

FindMin( (void *)iArr, sizeof(iArr)/sizeof(iArr[0]), sizeof(iArr[0]), (void *)&iMin, cmp_int );

FindMin( (void *)fArr, sizeof(fArr)/sizeof(fArr[0]), sizeof(fArr[0]), (void *)&fMin, cmp_double);

printf("Min of iArr is %d

", iMin);

printf("Min of fArr is %lf

", fMin);

return 0;

}

后记:

感谢所有帮助解决这个问题了朋友!这问题纠结了我两天,通过这个感觉对指针的理解又多了一点。

CSDN上的hikaliv建议看下va_list,va_args等看下可变参数如何读取,顺便理解了下,其实之前Sandy跟我说过,只

是一直没看,现在看了感觉收获蛮大。Felix021说我的代码风格不好,开始不明白,不过等他和Sandy写出来他们写的FindMin后,就明白了,

我的代码太晦涩了,还是写个自己看的,别人看了看不出大概,真的多注意了。

虽然,在C语言中实现模板不是不可能,但是我还是倾向于用模板,C++我了解的太少了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/303198.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lamp架构优化

LAMP&#xff08;LinuxApacheMysqlPerl/PHP/Python&#xff09;常用来搭建动态网站或者服务器的开源软件&#xff0c;本身都是各自独立的程序&#xff0c;但是因为常被放在一起使用&#xff0c;拥有了越来越高的兼容度&#xff0c;共同组成了一个强大的Web应用程序平台。每个LA…

毕业的那天,程序员师兄竟然让我去做这一行

给大家看一份最新的数据&#xff1a;&#xff08;薪资表&#xff09;2018年最新数据&#xff1a;python、大数据、人工智能从业者工资为什么人工智能行业的工资那么高&#xff1f;无论是科研院所&#xff0c;商业巨头还是初创企业&#xff0c;各行各业都在大力开发或者引进人工…

C# FileSystemWatcher文件监控实例

FileSystemWatcher可以使用FileSystemWatcher组件监视文件系统&#xff0c;并对文件系统的改变作出反应。通过使用FileSystemWatcher组件&#xff0c;在特定的文件或目录被创建、修改或删除时&#xff0c;可以快速和便捷地启动业务流程。例如&#xff0c;如果一组用户在合作处理…

c语言中二重指针如何赋值,关于二重指针释放的有关问题

关于二重指针释放的问题使用 malloc 来申请内存&#xff0c;申请的代码部分如下&#xff1a;float *fp (float*)malloc(sizeof(float)* h * w);float **cost (float**)malloc(sizeof(float*)* h);for (int i 0; i cost[i] &fp[i * w];}那我释放的时候应该如何释放呢&am…

第一个Win32 SDK应用程序

#include<windows.h>int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int);LRESULT WINAPI WndProc(HWND,UINT,WPARAM,LPARAM);// WinMain 函数int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){HWND hWnd; // 主窗口句柄…

Python 分析《爱情公寓》电影,让我十年情怀一瞬间都喂了狗

深陷抄袭之名、诉讼纠纷的《爱情公寓》终于上映了。情怀粉们的力量不容小觑&#xff0c;截止到8月12&#xff0c;《爱情公寓》票房已经突破3.72亿大关&#xff0c;稳坐票房冠军的宝座&#xff0c;院线排片占比高达40.0%。和超高票房背道而驰的&#xff0c;是各大社交平台上一边…

银行背景下分库分表技术选型

业务持续增长带来的单表数据量过大&#xff0c;必然影响到数据库的读写性能&#xff0c;那到底要不要分库分表呢&#xff1f;阿里巴巴P3C规范给出一个推荐&#xff1a;【推荐】单表行数超过500万行或者单表容量超过2GB&#xff0c;才推荐进行分库分表。说明&#xff1a;如果预计…

单片机矩阵消抖延时c语言,单片机矩阵按键定时器消抖程序源码

芯片是采用的stc89c51单片机.下面是矩阵键盘的电路图&#xff0c;矩阵键盘是接在p2口的.下面是单片机部分的图,数码管显示等完整的原理图可以从http://www.51hei.com/f/ks51.pdf 这里下载本文引用地址&#xff1a;http://www.eepw.com.cn/article/201612/324612.htm下面是程序源…

资料分享 | R语言资料分享来袭

小编从大学开始&#xff0c;便开启资料收集功能。R作为一种统计分析软件&#xff0c;广泛应用于生物、医学、电商、新闻等数据相关行业&#xff0c;并已成为主流数据应用软件之一&#xff0c;经过近几年的积累和沉淀&#xff0c;再加上日常的深入研究&#xff0c;小编收集整理了…

google code for xbmc addons2

2019独角兽企业重金招聘Python工程师标准>>> http://code.google.com/p/xbmc-skin-convergence/downloads/list 转载于:https://my.oschina.net/u/174445/blog/33955

ASP.NET Core中使用令牌桶限流

在限流时一般会限制每秒或每分钟的请求数&#xff0c;简单点一般会采用计数器算法&#xff0c;这种算法实现相对简单&#xff0c;也很高效&#xff0c;但是无法应对瞬时的突发流量。比如限流每秒100次请求&#xff0c;绝大多数的时间里都不会超过这个数&#xff0c;但是偶尔某一…

c语言怎样表示运行时间,C语言运行时间

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼#include#include#define C 90000000void main(){long int i;printf(" OOOOOOO ****** ****** OOO OOO \n");for(i0;i<C;i);printf(" OOOOOOO …

10.26

1. 去掉工程中添加的新类 2. DirectUI 3. MFC 文件操作 4. http://www.cnblogs.com/jiaxiaoai/archive/2011/07/04/2097188.html 5. ATL,COM 6. 全局变量转载于:https://www.cnblogs.com/shiney/archive/2011/10/26/2224799.html

我们需要什么样的开源教育?

点击上方“开源社”关注我们| 作者&#xff1a;庄表伟| 编辑&#xff1a;沈于蓝| 设计&#xff1a;宋传琪‍文章缘起‍写这篇文章的原因&#xff0c;是和几个朋友的闲聊。再之前&#xff0c;是因为看到了几篇文章2020-12-18 《欢迎加入&#xff01;开放原子大学首批开源讲师认证…

谷歌十年,我的认知被彻底颠覆

这个问题是提给自己的&#xff0c;算是对我 Google 十年的一个小结。强调“不装逼”&#xff0c;主要是提醒自己不要陷入下列俗套&#xff1a;离职后靠黑前雇主、八卦前雇主、揭秘前雇主搏出位&#xff08;这条是恶行&#xff09;满足大众猎奇心理吹嘘自己&#xff0c;贬低他人…

重言式判别c语言中文网,重言式判别 重言式判别源码及课程设计 c语言版.doc

重言式判别 重言式判别源码及课程设计 c语言版重言式的判别 题目&#xff1a;一个逻辑表达式如果对于其変元的任一种取值都为真&#xff0c;则称为重言式&#xff1b;反之&#xff0c;如果对于其变元的任一种取值都为假&#xff0c;则称为矛盾式&#xff1b;然而&#xff0c;更…

redis灵魂拷问:19图+11题带你面试通关

又到了金三银四跳槽季&#xff0c;好多同学已经开始行动了。今天我来助力一把&#xff0c;送出这套redis面试题&#xff0c;助力大家通关。1 redis为什么响应快1.1数据保存在内存中redis数据保存在内存中&#xff0c;读写操作只要访问内存&#xff0c;不需要磁盘IO。1.2.底层数…

趣图:BAT程序员的一天对比

这是一张流传网络的BAT的一天对比图&#xff1a;你看懂了吗&#xff1f;数据与算法之美用数据解决不可能长按扫码关注

android view存储为jpg,Android长按imageview把图片保存到本地的实例代码

工具类之前用AsyncTask现在改用rxJavapublic class SaveImageUtils {public static void imageSave(final ImageView imageView, final int id) {Observable.create(new Observable.OnSubscribe() {Overridepublic void call(Subscriber super ImageView> sub) {sub.onNext(…

【配置文件】大家来谈谈这个log4j到底有什么用?

一直在WEB项目中添加log4j.properties文件&#xff0c;却不知道该怎么使用它&#xff0c;希望大家不吝赐教 写日志&#xff0c;还能干什么&#xff1f; log4j.properties 主要用于配置那些范围会采用日记的记录&#xff0c;以及日记的输出格式&#xff0c;采用什么设备输出&…