火车票系统(C语言)【报告版-带截图】_第1页
火车票系统(C语言)【报告版-带截图】_第2页
火车票系统(C语言)【报告版-带截图】_第3页
火车票系统(C语言)【报告版-带截图】_第4页
火车票系统(C语言)【报告版-带截图】_第5页
已阅读5页,还剩41页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、火车票务管理系统第46页 共46页课题内容和要求设计目标:设计一个火车票务管理系统,用C语言实现。对该系统的要求如下:需求描述:1.系统功能(1)火车时刻信息录入。包括车次、日期、起点、终点、开车时间、到达时间、票价。数据存入数据文件hchsk.dat或hchsk.txt中。(2)火车时刻信息查询。按照车次查询按终点查询按起点查询按终点和日期查询 (3)统计 按终点统计每日的车次数 按起点统计每日的车次数2. 系统主界面应有以下功能选项录入火车时刻信息查询火车时刻信息统计火车车次退出注:建议用VC环境开发用户界面。结果形式:提交程序设计报告和可演示的系统软件需求分析系统主界面应有以下功能选项录

2、入火车时刻信息查询火车时刻信息统计火车车次退出(1)火车时刻信息录入。包括车次、日期、起点、终点、开车时间、到达时间、票价。数据存入数据文件hchsk.dat或hchsk.txt中。(2)火车时刻信息查询。按照车次查询按终点查询按起点查询按终点和日期查询 (3)统计 按终点统计每日的车次数 按起点统计每日的车次数在该部分中叙述每个模块的功能要求概要设计/* 头文件 */#include#include #include/* 函数声明 */int MENU(); /菜单模块void INPUT(); /输入模块void SEARCH(); /查询模块void SELECT(); /统计模块voi

3、d WRITE(); /文件记录模块void PRINT(); /打印模块void READ(); /文件读取模块void WRONG(); /纠错模块/* 宏定义 */#define N 1000 /最大列车组数#define Print %s%13s%10s%10s%12s%15s%11sn,Ti.code,Ti.data,Ti.lp,Ti.ap,Ti.lt,Ti.at,Ti.price /输出记录格式#define Wrong n ! ! ! ! ! ! ! ! ! ! ! 输入错误,请重新输入 ! ! ! ! ! ! ! ! ! ! ! n #define GeShi 车次 日期 起点

4、 终点 开车时间 到达时间 票价n /输出表头/* 结构定义 */struct Trainchar code10; /车次char data10; /日期char lp10; /left place 起点char ap10; /arrive place 终点char lt10; /left time 开车时间char at10; /arrive time 到达时间char price10; / 票价TN;/* 全局变量 */int i=0; /输入计数器int n=0; /合计计数器char l20; /纠错空间四、详细设计/* 头文件 */#include#include #include/

5、* 函数声明 */int MENU(); /菜单模块void INPUT(); /输入模块void SEARCH(); /查询模块void SELECT(); /统计模块void WRITE(); /文件记录模块void PRINT(); /打印模块void READ(); /文件读取模块void WRONG(); /纠错模块/* 宏定义 */#define N 1000 /最大列车组数#define Print %s%13s%10s%10s%12s%15s%11sn,Ti.code,Ti.data,Ti.lp,Ti.ap,Ti.lt,Ti.at,Ti.price /输出记录格式#define

6、 Wrong n ! ! ! ! ! ! ! ! ! ! ! 输入错误,请重新输入 ! ! ! ! ! ! ! ! ! ! ! n #define GeShi 车次 日期 起点 终点 开车时间 到达时间 票价n /输出表头/* 结构定义 */struct Trainchar code10; /车次char data10; /日期char lp10; /left place 起点char ap10; /arrive place 终点char lt10; /left time 开车时间char at10; /arrive time 到达时间char price10; / 票价TN;/* 全局变量

7、*/int i=0; /输入计数器int n=0; /合计计数器char l20; /纠错空间/主函数int main() int x;dox=MENU();switch(x)case 1:INPUT(); break;case 2:SEARCH(); break;case 3:SELECT(); break;case 4:printf(n );printf(n 正在退出,请按任意键退出);printf(nn 阿哈工作室出品 B09020424);printf(n n );break;default:WRONG(); break;while(x!=4);return 0;/菜单模块int ME

8、NU()int choice;system(cls); /借鉴而来的清屏语句,好用!printf(n n);printf( n);printf( n);printf( 欢迎进入火车票务管理系统 n);printf( n);printf( n);printf( nn);printf( n 请输入选项 n);printf( 1.录入火车时刻信息n);printf( 2.查询火车时刻信息n);printf( 3.统计火车车次n);printf( 4.退出n);printf( n);printf( 请选择: );scanf(%d,&choice);gets(l);return choice;/输入模块

9、void INPUT()char ch;dosystem(cls); /借鉴而来的清屏语句,好用!printf(nn =欢迎进入 录入火车时刻信息 界面=n);doprintf(n 新建列车信息?(y/n) );scanf(%c,&ch);gets(l);if(ch!=y&ch!=n)WRONG ();while(ch!=y&ch!=n);if(ch=n)break;printf( );printf(n 录入第%d趟列车信息n,n+1);printf( 车次 );gets(Ti.code);printf( 日期 );gets(Ti.data);printf( 起点 );gets(Ti.lp);

10、printf( 终点 );gets(Ti.ap);printf( 开车时间 );gets(Ti.lt);printf( 到达时间 );gets(Ti.at);printf( 票价 );gets(Ti.price);n+;i+;while(iN);WRITE();system(cls); /借鉴而来的清屏语句,好用!printf(n);printf( n);printf( 系统记载的列车信息如下n);printf( nn);PRINT();/文件记录模块void WRITE() FILE *fp,*fp1;if(fp=fopen(hchsk.txt,w)=NULL)printf(无法创建文件!n

11、n);getchar();return;if(fp1=fopen(N.txt,w)=NULL)/用于记录共几趟车printf(无法创建文件!n);getchar();return; for(i=0;in;i+)if(fwrite(&Ti,sizeof(struct Train),1,fp)=0)printf(写入数据失败!nn);fprintf(fp1,%d,n);fclose(fp);fclose(fp1); /文件读取模块void READ()FILE *fp,*fp1;if(fp=fopen(hchsk.txt,r)=NULL)printf(无法打开文件n);getchar();retu

12、rn;if(fp1=fopen(N.txt,r)=NULL)printf(无法打开文件!nn);getchar();return;fscanf(fp1,%d,&n);fclose(fp1);for(i=0;in;i+) fread(&Ti,sizeof(struct Train),1,fp); fclose(fp);/打印模块void PRINT()char ch10;READ();printf(n =n);printf(GeShi);for(i=0;in;i+) printf(Print);printf( =n);printf(n 按任意键返回上级菜单: );gets(ch);/查询模块vo

13、id SEARCH()char name110;/车次char name210;/终点char name310;/起点char name4110;/终点char name4210;/日期char choice;int j;int k;dosystem(cls); /借鉴而来的清屏语句,好用!printf(nn =欢迎进入 查询火车时刻信息 界面=n);printf(n +请选择查找方式+nn);printf( 1.显示所有列车信息 2.按车次查询 3.按终点查询n);printf( 4.按起点查询 5.按终点和日期查询 6.返回上级菜单n);printf(n 请选择: );scanf(%d,&

14、j);gets(l);switch(j)case 1:PRINT();break;case 2:printf(n 请输入车次 : );gets(name1);break;case 3:printf(n 请输入终点 : );gets(name2);break;case 4:printf(n 请输入起点 : );gets(name3);break;case 5:printf(n 请输入终点 : );gets(name41);printf(n 请输入日期 : );gets(name42);break;case 6: break;default:WRONG ();break;READ();k=0;if

15、(j=2)for(i=0;in;i+) if(strcmp(Ti.code,name1)=0)printf(n 按 车次 查找列车信息成功!n);printf(n =n);printf(GeShi);break;for(i=0;i 共查询到 %d 趟列车信息,请按任意键返回 - ,k);gets(l);if(j=3)for(i=0;in;i+) if(strcmp(Ti.ap,name2)=0)printf(n 按 终点 查找列车信息成功!n);printf(n =n);printf(GeShi);break;for(i=0;i 共查询到 %d 趟列车信息,请按任意键返回 - ,k);gets

16、(l);if(j=4)for(i=0;in;i+) if(strcmp(Ti.lp,name3)=0)printf(n 按 起点 查找列车信息成功!n);printf(n =n);printf(GeShi);break;for(i=0;i 共查询到 %d 趟列车信息,请按任意键返回 - ,k);gets(l);if(j=5)for(i=0;in;i+) if(strcmp(Ti.ap,name41)=0&strcmp(Ti.data,name42)=0)printf(n 按 终点和日期 查找列车信息成功!n);printf(n =n);printf(GeShi);break;for(i=0;i

17、 共查询到 %d 趟列车信息,请按任意键返回 继续查找(1),返回主菜单(2)- );choice=getchar();gets(l);if(choice!=1&choice!=2)WRONG ();while(choice!=1&choice!=2);while(choice!=2);/统计模块void SELECT()char name110;/终点char name210;/起点int t,k;char choice;dosystem(cls);printf(nn =欢迎进入 统计火车车次 界面=n);printf(n -|- -|- -|- -|- 请选择统计方式 -|- -|- -|

18、- -|- nn);printf( 1.按终点统计每日的车次数n);printf( 2.按起点统计每日的车次数n);printf(n 请选择: );scanf(%d,&t);gets(l);switch(t)case 1:printf( 请输入终点: );gets(name1);break;case 2:printf( 请输入起点: );gets(name2);break;default:WRONG();break;READ();if(t=1)k=0;for(i=0;in;i+) if(strcmp(Ti.lp,name1)=0)printf(n 按 终点 统计列车信息成功!n);break;

19、for(i=0;i 共查询到 %d 趟列车信息,请按任意键返回 - ,k);gets(l);if(t=2)k=0;for(i=0;in;i+) if(strcmp(Ti.lp,name2)=0)printf(n 按 起点 统计列车信息成功!n);break;for(i=0;i 共查询到 %d 趟列车信息,请按任意键返回 继续统计(1),返回主菜单(2) ! ! ! 输入错误,请按任意键返回 ! ! ! );gets(l);五、测试数据及其结果分析1、菜单模块-选择 界面:1、菜单模块-退出2、输入模块2、输入完毕3、查询模块3、查询模块-13、查询模块-23、查询模块-33、查询模块-43、查

20、询模块-53、查询模块-询问3、查询模块-失败4、统计模块-14、统计模块-24、统计模块询问出错提示六、调试过程中的问题实验中遇到的问题不少,有以下几点:本来一开始做程序时候想使用模块化的,但因外部变量总要声明的缘故被取消。对于需要选择的时候,比如在菜单模块中,如果键入一些非正常的字符,就会是系统出错无法运行。因此,我加入了纠错模块“WRONG();”。在输出格式上面很难控制,几经调试才最后成功。并且引入宏 #define Print 加以控制。在结构定义时本想引入动态空间加以控制,但因不好控制最终定义为#define N 1000 。最难得在与数据的写入与读取WRITE(); READ()

21、; 最后为了保证准确记录列车的趟数不得不加入了文件N(fp1),同时,由于在for(;)循环中没有把“in” 和“i=n”严格区分,在早期运行时会莫名其妙多打出一行,最终发现并修改好。在编写函数SEARCH();时犯了一个低级错误,把“=” 和“=”混淆,几经调试才发现,真是笨。最后就是统计函数SELECT();,虽然至今仍然觉得这个函数编写的似乎不合题意,但也没办法啦。值得一提的是,他和SEARCH();函数一样,使用了两次循环,虽然累赘,但是保证输出时的简洁与明了。七、程序设计总结经过这为期两周的集中化训练,我的编程能力有了较明显的提高。然而,虽然只是编写这么一个小小的程序,却是我意识到自

22、己的不足以及需要提高的地方。首先,程序语句的不熟练使我在编程初期工作效率相当的低下。不断地翻书让我很“痛苦”。第二,对于流与文件的不熟悉让我在编写读取与写入文件那两个模块时非常迟缓。不得不说明的是,我也是参考了其他同学的程序时才真正学会了文件的操作。第三,在编程初期使用模块化的程序思想使我效率低下,相比之下的函数与宏的定义更适合于这种小型程序。第四,本次编程最令我高兴的是在借鉴其他同学的代码时发现了system(cls); 这一语句。清屏语句的引入使程序执行时无比简洁。没有了一行有一行的烦恼。这也显示出了我对此语言了解之浅薄,我仍需努力才行。最后,通过这两周的编程,我感觉到了程序的来之不易。平

23、时惯用的免费软件没觉得有什么,但通过本次的编程后我感觉到了编程者的艰辛。这两周我学到了很多,也懂得了很多。感谢这两周的程序设计课,我的C语言又有了新的提高。附录资料:不需要的可以自行删除C语言中如何获取时间?精度如何?1 使用time_t time( time_t * timer ) 精确到秒2 使用clock_t clock() 得到的是CPU时间精确到1/CLOCKS_PER_SEC秒3 计算时间差使用double difftime( time_t timer1, time_t timer0 )4 使用DWORD GetTickCount() 精确到毫秒5 如果使用MFC的CTime类,可

24、以用CTime:GetCurrentTime() 精确到秒6 要获取高精度时间,可以使用BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency)获取系统的计数器的频率BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount)获取计数器的值然后用两次计数器的差除以Frequency就得到时间。7 Multimedia Timer FunctionsThe following functions are used with multimedia timers.timeBe

25、ginPeriod/timeEndPeriod/timeGetDevCaps/timeGetSystemTime/*/用标准C实现获取当前系统时间的函数一.time()函数time(&rawtime)函数获取当前时间距1970年1月1日的秒数,以秒计数单位,存于rawtime 中。#include time.hvoid main ()time_t rawtime;struct tm * timeinfo;time ( &rawtime );timeinfo = localtime ( &rawtime );printf ( 007The current date/time is: %s, as

26、ctime (timeinfo) );exit(0);=#include - 必须的时间函数头文件time_t - 时间类型(time.h 定义是typedef long time_t; 追根溯源,time_t是long)struct tm - 时间结构,time.h 定义如下:int tm_sec;int tm_min;int tm_hour;int tm_mday;int tm_mon;int tm_year;int tm_wday;int tm_yday;int tm_isdst;time ( &rawtime ); - 获取时间,以秒计,从1970年1月一日起算,存于rawtimelo

27、caltime ( &rawtime ); - 转为当地时间,tm 时间结构asctime ()- 转为标准ASCII时间格式:星期 月 日 时:分:秒 年二.clock()函数,用clock()函数,得到系统启动以后的毫秒级时间,然后除以CLOCKS_PER_SEC,就可以换成“秒”,标准c函数。clock_t clock ( void );#includeclock_t t = clock();long sec = t / CLOCKS_PER_SEC;他是记录时钟周期的,实现看来不会很精确,需要试验验证;三.gettime(&t); 据说tc2.0的time结构含有毫秒信息#includ

28、e#includeint main(void)struct time t;gettime(&t);printf(The current time is: -:d:d.dn,t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);return 0;time 是一个结构体, 其中成员函数 ti_hund 是毫秒。四.GetTickCount(),这个是windows里面常用来计算程序运行时间的函数;DWORD dwStart = GetTickCount();/这里运行你的程序代码DWORD dwEnd = GetTickCount();则(dwEnd-dwStart)

29、就是你的程序运行时间, 以毫秒为单位这个函数只精确到55ms,1个tick就是55ms。五.timeGetTime()t,imeGetTime()基本等于GetTickCount(),但是精度更高DWORD dwStart = timeGetTime();/这里运行你的程序代码DWORD dwEnd = timeGetTime();则(dwEnd-dwStart)就是你的程序运行时间, 以毫秒为单位虽然返回的值单位应该是ms,但传说精度只有10ms。=/*Unix#unix时间相关,也是标准库的/*1.timegm函数只是将struct tm结构转成time_t结构,不使用时区信息;time_

30、t timegm(struct tm *tm);2.mktime使用时区信息time_t mktime(struct tm *tm);timelocal 函数是GNU扩展的与posix函数mktime相当time_t timelocal (struct tm *tm);3.gmtime函数只是将time_t结构转成struct tm结构,不使用时区信息;struct tm * gmtime(const time_t *clock);4.localtime使用时区信息struct tm * localtime(const time_t *clock);1.time获取时间,stime设置时间ti

31、me_t t;t = time(&t);2.stime其参数应该是GMT时间,根据本地时区设置为本地时间;int stime(time_t *tp)3.UTC=true 表示采用夏时制;4.文件的修改时间等信息全部采用GMT时间存放,不同的系统在得到修改时间后通过localtime转换成本地时间;5.设置时区推荐使用setup来设置;6.设置时区也可以先更变/etc/sysconfig/clock中的设置再将ln -fs /usr/share/zoneinfo/xxxx/xxx /etc/localtime 才能重效time_t只能表示68年的范围,即mktime只能返回1970-2038这一

32、段范围的time_t看看你的系统是否有time_t64,它能表示更大的时间范围/*windows#Window里面的一些不一样的/*一.CTime () 类VC编程一般使用CTime类 获得当前日期和时间CTime t = GetCurrentTime();SYSTEMTIME 结构包含毫秒信息typedef struct _SYSTEMTIME WORD wYear;WORD wMonth;WORD wDayOfWeek;WORD wDay;WORD wHour;WORD wMinute;WORD wSecond;WORD wMilliseconds; SYSTEMTIME, *PSYSTE

33、MTIME;SYSTEMTIME t1;GetSystemTime(&t1)CTime curTime(t1);WORD ms = t1.wMilliseconds;SYSTEMTIME sysTm;:GetLocalTime(&sysTm);在time.h中的_strtime() /只能在windows中用char t11;_strtime(t);puts(t);/*获得当前日期和时间CTime tm=CTime:GetCurrentTime();CString str=tm.Format(%Y-%m-%d);在VC中,我们可以借助CTime时间类,获取系统当前日期,具体使用方法如下:CTi

34、me t = CTime:GetCurrentTime(); /获取系统日期,存储在t里面int d=t.GetDay(); /获得当前日期int y=t.GetYear(); /获取当前年份int m=t.GetMonth(); /获取当前月份int h=t.GetHour(); /获取当前为几时int mm=t.GetMinute(); /获取当前分钟int s=t.GetSecond(); /获取当前秒int w=t.GetDayOfWeek(); /获取星期几,注意1为星期天,7为星期六二.CTimeSpan类如果想计算两段时间的差值,可以使用CTimeSpan类,具体使用方法如下:C

35、Time t1( 1999, 3, 19, 22, 15, 0 );CTime t = CTime:GetCurrentTime();CTimeSpan span=t-t1; /计算当前系统时间与时间t1的间隔int iDay=span.GetDays(); /获取这段时间间隔共有多少天int iHour=span.GetTotalHours(); /获取总共有多少小时int iMin=span.GetTotalMinutes();/获取总共有多少分钟int iSec=span.GetTotalSeconds();/获取总共有多少秒三._timeb()函数_timeb定义在SYSTIMEB.H

36、,有四个fieldsdstflagmillitmtimetimezonevoid _ftime( struct _timeb *timeptr );struct _timeb timebuffer;_ftime( &timebuffer );取当前时间:文档讲可以到ms,有人测试,好象只能到16ms!四.设置计时器定义TIMER ID#define TIMERID_JISUANFANGSHI 2在适当的地方设置时钟,需要开始其作用的地方;SetTimer(TIMERID_JISUANFANGSHI,200,NULL);在不需要定时器的时候的时候销毁掉时钟KillTimer(TIMERID_JI

37、SUANFANGSHI);对应VC程序的消息映射void CJisuan:OnTimer(UINT nIDEvent)switch(nIDEvent)#如何设定当前系统时间windowsSYSTEMTIME m_myLocalTime,*lpSystemTime;m_myLocalTime.wYear=2003;m_myLocalTime.wM;m_myLocalTime.wDay=1;m_myLocalTime.wHour=0;m_myLocalTime.wMinute=0;m_myLocalTime.wSec;m_myLocalTime.wMillisec;lpSystemTime=&m_

38、myLocalTime;if( SetLocalTime(lpSystemTime) ) /此处换成 SetSystemTime( )也不行MessageBox(OK !);elseMessageBox(Error !);SYSTEMTIME m_myLocalTime,*lpSystemTime;m_myLocalTime.wYear=2003;m_myLocalTime.wM;m_myLocalTime.wDay=1;lpSystemTime=&m_myLocalTime;if( SetDate(lpSystemTime) ) /此处换成 SetSystemTime( )也不行Messag

39、eBox(OK !);elseMessageBox(Error !);本文来自CSDN博客,转载请标明出处:HYPERLINK /khuang2008/archive/2008/12/09/3483274.aspx/khuang2008/archive/2008/12/09/3483274.aspx一种制作微秒级精度定时器的方法当使用定时器时,在很多情况下只用到毫秒级的时间间隔,所以只需用到下面的两种常用方式就满足要求了。一是用SetTimer函数建立一个定时器后,在程序中通过处理由定时器发送到线程消息队列中的WM_TIMER消息,而得到定时的效果(退出程序时别忘了调用和SetTimer配对使

40、用的KillTimer函数)。二是利用GetTickCount函数可以返回自计算机启动后的时间,通过两次调用GetTickCount函数,然后控制它们的差值来取得定时效果,此方式跟第一种方式一样,精度也是毫秒级的。用这两种方式取得的定时效果虽然在许多场合已经满足实际的要求,但由于它们的精度只有毫秒级的,而且在要求定时时间间隔小时,实际定时误差大。下面介绍一种能取得高精度定时的方法。在一些计算机硬件系统中,包含有高精度运行计数器(high-resolution performance counter),利用它可以获得高精度定时间隔,其精度与CPU的时钟频率有关。采用这种方法的步骤如下:1、首先调

41、用QueryPerformanceFrequency函数取得高精度运行计数器的频率f。单位是每秒多少次(n/s),此数一般很大。2、在需要定时的代码的两端分别调用QueryPerformanceCounter以取得高精度运行计数器的数值n1,n2。两次数值的差值通过f换算成时间间隔,t=(n2-n1)/f。下面举一个例子来演示这种方法的使用及它的精确度。在VC 6.0 下用MFC建立一个对话框工程,取名为HightTimer.在对话框面板中控件的布局如下图:其中包含两个静态文本框,两个编辑框和两个按纽。上面和下面位置的编辑框的ID分别为IDC_E_TEST和IDC_E_ACTUAL,通过MFC

42、 ClassWizard添加的成员变量也分别对应为DWORD m_dwTest和DWORD m_dwAct. “退出”按纽的ID为IDOK,“开始测试”按纽ID为IDC_B_TEST,用MFC ClassWizard添加此按纽的单击消息处理函数如下:void CHightTimerDlg:OnBTest()/ TODO: Add your control notification handler code hereUpdateData(TRUE); /取输入的测试时间值到与编辑框相关联的成员变量m_dwTest中LARGE_INTEGER frequence;if(!QueryPerforma

43、nceFrequency( &frequence) /取高精度运行计数器的频率,若硬件不支持则返回FALSEMessageBox(Your computer hardware doesnt support the high-resolution performance counter,Not Support, MB_ICONEXCLAMATION | MB_OK);LARGE_INTEGER test, ret;test.QuadPart = frequence.QuadPart * m_dwTest / 1000000; /通过频率换算微秒数到对应的数量(与CPU时钟有关),1秒=10000

44、00微秒ret = MySleep( test ); /调用此函数开始延时,返回实际花销的数量m_dwAct = (DWORD)(1000000 * ret.QuadPart / frequence.QuadPart ); /换算到微秒数UpdateData(FALSE); /显示到对话框面板其中上面调用的MySleep函数如下:LARGE_INTEGER CHightTimerDlg:MySleep(LARGE_INTEGER Interval)/ 功能:执行实际的延时功能 / 参数:Interval 参数为需要执行的延时与时间有关的数量 / 返回值:返回此函数执行后实际所用的时间有关的数量

45、 / LARGE_INTEGER privious, current, Elapse;QueryPerformanceCounter( &privious );current = privious;while( current.QuadPart - privious.QuadPart Interval.QuadPart )QueryPerformanceCounter( t );Elapse.QuadPart = current.QuadPart - privious.QuadPart;return Elapse;注:别忘了在头文件中为此函数添加函数声明。至此,可以编译和执行此工程了,结果如上

46、图所示。在本人所用的机上(奔腾366, 64M内存)测试,当测试时间超过3微秒时,准确度已经非常高了,此时机器执行本身延时函数代码的时间对需要延时的时间影响很小了。上面的函数由于演示测试的需要,没有在函数级封装,下面给出的函数基本上可以以全局函数的形式照搬到别的程序中。BOOL MySleep(DWORD dwInterval)/ 功能:执行微秒级的延时功能 / 参数:Interval 参数为需要的延时数(单位:微秒) / 返回值:若计算机硬件不支持此功能,返回FALSE,若函数执行成功,返回TRUE / BOOL bNormal = TRUE;LARGE_INTEGER frequence,

47、 privious, current, interval;if(!QueryPerformanceFrequency( &frequence):MessageBox(NULL, Your computer hardware doesnt support the high-resolution performance counter,Not Support, MB_ICONEXCLAMATION | MB_OK); /或其它的提示信息return FALSE;interval.QuadPart = frequence.QuadPart * dwInterval / 1000000;bNormal

48、 = bNormal & QueryPerformanceCounter( &privious );current = privious;while( current.QuadPart - privious.QuadPart interval.QuadPart )bNormal = bNormal & QueryPerformanceCounter( t );return bNormal;需要指出的是,由于在此函数中的代码很多,机器在执行这些代码所花费的时间也很长,所以在需要几个微秒的延时时,会影响精度。实际上,读者在熟悉这种方法后,只要使用QueryPerformanceFrequency和

49、QueryPerformanceCounter这两个函数就能按实际需要写出自己的延时代码了。使用CPU时间戳进行高精度计时对关注性能的程序开发人员而言,一个好的计时部件既是益友,也是良师。计时器既可以作为程序组件帮助程序员精确的控制程序进程,又是一件有力的调试武器,在有经验的程序员手里可以尽快的确定程序的性能瓶颈,或者对不同的算法作出有说服力的性能比较。在Windows平台下,常用的计时器有两种,一种是timeGetTime多媒体计时器,它可以提供毫秒级的计时。但这个精度对很多应用场合而言还是太粗糙了。另一种是QueryPerformanceCount计数器,随系统的不同可以提供微秒级的计数。

50、对于实时图形处理、多媒体数据流处理、或者实时系统构造的程序员,善用QueryPerformanceCount/QueryPerformanceFrequency是一项基本功。本文要介绍的,是另一种直接利用Pentium CPU内部时间戳进行计时的高精度计时手段。以下讨论主要得益于Windows图形编程一书,第15页17页,有兴趣的读者可以直接参考该书。关于RDTSC指令的详细讨论,可以参考Intel产品手册。本文仅仅作抛砖之用。在Intel Pentium以上级别的CPU中,有一个称为“时间戳(Time Stamp)”的部件,它以64位无符号整型数的格式,记录了自CPU上电以来所经过的时钟周期

51、数。由于目前的CPU主频都非常高,因此这个部件可以达到纳秒级的计时精度。这个精确性是上述两种方法所无法比拟的。在Pentium以上的CPU中,提供了一条机器指令RDTSC(Read Time Stamp Counter)来读取这个时间戳的数字,并将其保存在EDX:EAX寄存器对中。由于EDX:EAX寄存器对恰好是Win32平台下C+语言保存函数返回值的寄存器,所以我们可以把这条指令看成是一个普通的函数调用。像这样:inline unsigned _int64 GetCycleCount() _asm RDTSC 但是不行,因为RDTSC不被C+的内嵌汇编器直接支持,所以我们要用_emit伪指令

52、直接嵌入该指令的机器码形式0X0F、0X31,如下:inline unsigned _int64 GetCycleCount() _asm _emit 0 x0F _asm _emit 0 x31 以后在需要计数器的场合,可以像使用普通的Win32 API一样,调用两次GetCycleCount函数,比较两个返回值的差,像这样: unsigned long t; t = (unsigned long)GetCycleCount(); /Do Something time-intensive . t -= (unsigned long)GetCycleCount(); Windows图形编程第1

53、5页编写了一个类,把这个计数器封装起来。有兴趣的读者可以去参考那个类的代码。作者为了更精确的定时,做了一点小小的改进,把执行RDTSC指令的时间,通过连续两次调用GetCycleCount函数计算出来并保存了起来,以后每次计时结束后,都从实际得到的计数中减掉这一小段时间,以得到更准确的计时数字。但我个人觉得这一点点改进意义不大。在我的机器上实测,这条指令大概花掉了几十到100多个周期,在Celeron 800MHz的机器上,这不过是十分之一微秒的时间。对大多数应用来说,这点时间完全可以忽略不计;而对那些确实要精确到纳秒数量级的应用来说,这个补偿也过于粗糙了。 这个方法的优点是: 1.高精度。可

54、以直接达到纳秒级的计时精度(在1GHz的CPU上每个时钟周期就是一纳秒),这是其他计时方法所难以企及的。 2.成本低。timeGetTime 函数需要链接多媒体库winmm.lib,QueryPerformance* 函数根据MSDN的说明,需要硬件的支持(虽然我还没有见过不支持的机器)和KERNEL库的支持,所以二者都只能在Windows平台下使用(关于DOS平台下的高精度计时问题,可以参考图形程序开发人员指南,里面有关于控制定时器8253的详细说明)。但RDTSC指令是一条CPU指令,凡是i386平台下Pentium以上的机器均支持,甚至没有平台的限制(我相信i386版本UNIX和Linux下这个方法同样适用,但没有条件试验),而且函数调用的开销是最小的。 3.具有和CPU主频直接对应的速率关系。一个计数相当于1/(CPU主频Hz数)秒,这样只要知道了CPU的主频,可以直接计算出时间。这和

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论