火車票系統(tǒng)(C語言)【報告版-帶截圖】_第1頁
火車票系統(tǒng)(C語言)【報告版-帶截圖】_第2頁
火車票系統(tǒng)(C語言)【報告版-帶截圖】_第3頁
火車票系統(tǒng)(C語言)【報告版-帶截圖】_第4頁
火車票系統(tǒng)(C語言)【報告版-帶截圖】_第5頁
已閱讀5頁,還剩41頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、火車票務管理系統(tǒng)第46頁 共46頁課題內容和要求設計目標:設計一個火車票務管理系統(tǒng),用C語言實現(xiàn)。對該系統(tǒng)的要求如下:需求描述:1.系統(tǒng)功能(1)火車時刻信息錄入。包括車次、日期、起點、終點、開車時間、到達時間、票價。數(shù)據(jù)存入數(shù)據(jù)文件hchsk.dat或hchsk.txt中。(2)火車時刻信息查詢。按照車次查詢按終點查詢按起點查詢按終點和日期查詢 (3)統(tǒng)計 按終點統(tǒng)計每日的車次數(shù) 按起點統(tǒng)計每日的車次數(shù)2. 系統(tǒng)主界面應有以下功能選項錄入火車時刻信息查詢火車時刻信息統(tǒng)計火車車次退出注:建議用VC環(huán)境開發(fā)用戶界面。結果形式:提交程序設計報告和可演示的系統(tǒng)軟件需求分析系統(tǒng)主界面應有以下功能選項錄

2、入火車時刻信息查詢火車時刻信息統(tǒng)計火車車次退出(1)火車時刻信息錄入。包括車次、日期、起點、終點、開車時間、到達時間、票價。數(shù)據(jù)存入數(shù)據(jù)文件hchsk.dat或hchsk.txt中。(2)火車時刻信息查詢。按照車次查詢按終點查詢按起點查詢按終點和日期查詢 (3)統(tǒng)計 按終點統(tǒng)計每日的車次數(shù) 按起點統(tǒng)計每日的車次數(shù)在該部分中敘述每個模塊的功能要求概要設計/* 頭文件 */#include#include #include/* 函數(shù)聲明 */int MENU(); /菜單模塊void INPUT(); /輸入模塊void SEARCH(); /查詢模塊void SELECT(); /統(tǒng)計模塊voi

3、d WRITE(); /文件記錄模塊void PRINT(); /打印模塊void READ(); /文件讀取模塊void WRONG(); /糾錯模塊/* 宏定義 */#define N 1000 /最大列車組數(shù)#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; /輸入計數(shù)器int n=0; /合計計數(shù)器char l20; /糾錯空間四、詳細設計/* 頭文件 */#include#include #include/

5、* 函數(shù)聲明 */int MENU(); /菜單模塊void INPUT(); /輸入模塊void SEARCH(); /查詢模塊void SELECT(); /統(tǒng)計模塊void WRITE(); /文件記錄模塊void PRINT(); /打印模塊void READ(); /文件讀取模塊void WRONG(); /糾錯模塊/* 宏定義 */#define N 1000 /最大列車組數(shù)#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; /輸入計數(shù)器int n=0; /合計計數(shù)器char l20; /糾錯空間/主函數(shù)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( 歡迎進入火車票務管理系統(tǒng) n);printf( n);printf( n);printf( nn);printf( n 請輸入選項 n);printf( 1.錄入火車時刻信息n);printf( 2.查詢火車時刻信息n);printf( 3.統(tǒng)計火車車次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( 系統(tǒng)記載的列車信息如下n);printf( nn);PRINT();/文件記錄模塊void WRITE() FILE *fp,*fp1;if(fp=fopen(hchsk.txt,w)=NULL)printf(無法創(chuàng)建文件!n

11、n);getchar();return;if(fp1=fopen(N.txt,w)=NULL)/用于記錄共幾趟車printf(無法創(chuàng)建文件!n);getchar();return; for(i=0;in;i+)if(fwrite(&Ti,sizeof(struct Train),1,fp)=0)printf(寫入數(shù)據(jù)失敗!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 趟列車信息,請按任意鍵返回 繼續(xù)查找(1),返回主菜單(2)- );choice=getchar();gets(l);if(choice!=1&choice!=2)WRONG ();while(choice!=1&choice!=2);while(choice!=2);/統(tǒng)計模塊void SELECT()char name110;/終點char name210;/起點int t,k;char choice;dosystem(cls);printf(nn =歡迎進入 統(tǒng)計火車車次 界面=n);printf(n -|- -|- -|- -|- 請選擇統(tǒng)計方式 -|- -|- -|

18、- -|- nn);printf( 1.按終點統(tǒng)計每日的車次數(shù)n);printf( 2.按起點統(tǒng)計每日的車次數(shù)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 按 終點 統(tǒng)計列車信息成功!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 按 起點 統(tǒng)計列車信息成功!n);break;for(i=0;i 共查詢到 %d 趟列車信息,請按任意鍵返回 繼續(xù)統(tǒng)計(1),返回主菜單(2) ! ! ! 輸入錯誤,請按任意鍵返回 ! ! ! );gets(l);五、測試數(shù)據(jù)及其結果分析1、菜單模塊-選擇 界面:1、菜單模塊-退出2、輸入模塊2、輸入完畢3、查詢模塊3、查詢模塊-13、查詢模塊-23、查詢模塊-33、查詢模塊-43、查

20、詢模塊-53、查詢模塊-詢問3、查詢模塊-失敗4、統(tǒng)計模塊-14、統(tǒng)計模塊-24、統(tǒng)計模塊詢問出錯提示六、調試過程中的問題實驗中遇到的問題不少,有以下幾點:本來一開始做程序時候想使用模塊化的,但因外部變量總要聲明的緣故被取消。對于需要選擇的時候,比如在菜單模塊中,如果鍵入一些非正常的字符,就會是系統(tǒng)出錯無法運行。因此,我加入了糾錯模塊“WRONG();”。在輸出格式上面很難控制,幾經(jīng)調試才最后成功。并且引入宏 #define Print 加以控制。在結構定義時本想引入動態(tài)空間加以控制,但因不好控制最終定義為#define N 1000 。最難得在與數(shù)據(jù)的寫入與讀取WRITE(); READ()

21、; 最后為了保證準確記錄列車的趟數(shù)不得不加入了文件N(fp1),同時,由于在for(;)循環(huán)中沒有把“in” 和“i=n”嚴格區(qū)分,在早期運行時會莫名其妙多打出一行,最終發(fā)現(xiàn)并修改好。在編寫函數(shù)SEARCH();時犯了一個低級錯誤,把“=” 和“=”混淆,幾經(jīng)調試才發(fā)現(xiàn),真是笨。最后就是統(tǒng)計函數(shù)SELECT();,雖然至今仍然覺得這個函數(shù)編寫的似乎不合題意,但也沒辦法啦。值得一提的是,他和SEARCH();函數(shù)一樣,使用了兩次循環(huán),雖然累贅,但是保證輸出時的簡潔與明了。七、程序設計總結經(jīng)過這為期兩周的集中化訓練,我的編程能力有了較明顯的提高。然而,雖然只是編寫這么一個小小的程序,卻是我意識到自

22、己的不足以及需要提高的地方。首先,程序語句的不熟練使我在編程初期工作效率相當?shù)牡拖?。不斷地翻書讓我很“痛苦”。第二,對于流與文件的不熟悉讓我在編寫讀取與寫入文件那兩個模塊時非常遲緩。不得不說明的是,我也是參考了其他同學的程序時才真正學會了文件的操作。第三,在編程初期使用模塊化的程序思想使我效率低下,相比之下的函數(shù)與宏的定義更適合于這種小型程序。第四,本次編程最令我高興的是在借鑒其他同學的代碼時發(fā)現(xiàn)了system(cls); 這一語句。清屏語句的引入使程序執(zhí)行時無比簡潔。沒有了一行有一行的煩惱。這也顯示出了我對此語言了解之淺薄,我仍需努力才行。最后,通過這兩周的編程,我感覺到了程序的來之不易。平

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)獲取系統(tǒng)的計數(shù)器的頻率BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount)獲取計數(shù)器的值然后用兩次計數(shù)器的差除以Frequency就得到時間。7 Multimedia Timer FunctionsThe following functions are used with multimedia timers.timeBe

25、ginPeriod/timeEndPeriod/timeGetDevCaps/timeGetSystemTime/*/用標準C實現(xiàn)獲取當前系統(tǒng)時間的函數(shù)一.time()函數(shù)time(&rawtime)函數(shù)獲取當前時間距1970年1月1日的秒數(shù),以秒計數(shù)單位,存于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 - 必須的時間函數(shù)頭文件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 ); - 轉為當?shù)貢r間,tm 時間結構asctime ()- 轉為標準ASCII時間格式:星期 月 日 時:分:秒 年二.clock()函數(shù),用clock()函數(shù),得到系統(tǒng)啟動以后的毫秒級時間,然后除以CLOCKS_PER_SEC,就可以換成“秒”,標準c函數(shù)。clock_t clock ( void );#includeclock_t t = clock();long sec = t / CLOCKS_PER_SEC;他是記錄時鐘周期的,實現(xiàn)看來不會很精確,需要試驗驗證;三.gettime(&t); 據(jù)說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 是一個結構體, 其中成員函數(shù) ti_hund 是毫秒。四.GetTickCount(),這個是windows里面常用來計算程序運行時間的函數(shù);DWORD dwStart = GetTickCount();/這里運行你的程序代碼DWORD dwEnd = GetTickCount();則(dwEnd-dwStart)

29、就是你的程序運行時間, 以毫秒為單位這個函數(shù)只精確到55ms,1個tick就是55ms。五.timeGetTime()t,imeGetTime()基本等于GetTickCount(),但是精度更高DWORD dwStart = timeGetTime();/這里運行你的程序代碼DWORD dwEnd = timeGetTime();則(dwEnd-dwStart)就是你的程序運行時間, 以毫秒為單位雖然返回的值單位應該是ms,但傳說精度只有10ms。=/*Unix#unix時間相關,也是標準庫的/*1.timegm函數(shù)只是將struct tm結構轉成time_t結構,不使用時區(qū)信息;time_

30、t timegm(struct tm *tm);2.mktime使用時區(qū)信息time_t mktime(struct tm *tm);timelocal 函數(shù)是GNU擴展的與posix函數(shù)mktime相當time_t timelocal (struct tm *tm);3.gmtime函數(shù)只是將time_t結構轉成struct tm結構,不使用時區(qū)信息;struct tm * gmtime(const time_t *clock);4.localtime使用時區(qū)信息struct tm * localtime(const time_t *clock);1.time獲取時間,stime設置時間ti

31、me_t t;t = time(&t);2.stime其參數(shù)應該是GMT時間,根據(jù)本地時區(qū)設置為本地時間;int stime(time_t *tp)3.UTC=true 表示采用夏時制;4.文件的修改時間等信息全部采用GMT時間存放,不同的系統(tǒng)在得到修改時間后通過localtime轉換成本地時間;5.設置時區(qū)推薦使用setup來設置;6.設置時區(qū)也可以先更變/etc/sysconfig/clock中的設置再將ln -fs /usr/share/zoneinfo/xxxx/xxx /etc/localtime 才能重效time_t只能表示68年的范圍,即mktime只能返回1970-2038這一

32、段范圍的time_t看看你的系統(tǒng)是否有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時間類,獲取系統(tǒng)當前日期,具體使用方法如下:CTi

34、me t = CTime:GetCurrentTime(); /獲取系統(tǒng)日期,存儲在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; /計算當前系統(tǒng)時間與時間t1的間隔int iDay=span.GetDays(); /獲取這段時間間隔共有多少天int iHour=span.GetTotalHours(); /獲取總共有多少小時int iMin=span.GetTotalMinutes();/獲取總共有多少分鐘int iSec=span.GetTotalSeconds();/獲取總共有多少秒三._timeb()函數(shù)_timeb定義在SYSTIMEB.H

36、,有四個fieldsdstflagmillitmtimetimezonevoid _ftime( struct _timeb *timeptr );struct _timeb timebuffer;_ftime( &timebuffer );取當前時間:文檔講可以到ms,有人測試,好象只能到16ms!四.設置計時器定義TIMER ID#define TIMERID_JISUANFANGSHI 2在適當?shù)牡胤皆O置時鐘,需要開始其作用的地方;SetTimer(TIMERID_JISUANFANGSHI,200,NULL);在不需要定時器的時候的時候銷毀掉時鐘KillTimer(TIMERID_JI

37、SUANFANGSHI);對應VC程序的消息映射void CJisuan:OnTimer(UINT nIDEvent)switch(nIDEvent)#如何設定當前系統(tǒng)時間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函數(shù)建立一個定時器后,在程序中通過處理由定時器發(fā)送到線程消息隊列中的WM_TIMER消息,而得到定時的效果(退出程序時別忘了調用和SetTimer配對使

40、用的KillTimer函數(shù))。二是利用GetTickCount函數(shù)可以返回自計算機啟動后的時間,通過兩次調用GetTickCount函數(shù),然后控制它們的差值來取得定時效果,此方式跟第一種方式一樣,精度也是毫秒級的。用這兩種方式取得的定時效果雖然在許多場合已經(jīng)滿足實際的要求,但由于它們的精度只有毫秒級的,而且在要求定時時間間隔小時,實際定時誤差大。下面介紹一種能取得高精度定時的方法。在一些計算機硬件系統(tǒng)中,包含有高精度運行計數(shù)器(high-resolution performance counter),利用它可以獲得高精度定時間隔,其精度與CPU的時鐘頻率有關。采用這種方法的步驟如下:1、首先調

41、用QueryPerformanceFrequency函數(shù)取得高精度運行計數(shù)器的頻率f。單位是每秒多少次(n/s),此數(shù)一般很大。2、在需要定時的代碼的兩端分別調用QueryPerformanceCounter以取得高精度運行計數(shù)器的數(shù)值n1,n2。兩次數(shù)值的差值通過f換算成時間間隔,t=(n2-n1)/f。下面舉一個例子來演示這種方法的使用及它的精確度。在VC 6.0 下用MFC建立一個對話框工程,取名為HightTimer.在對話框面板中控件的布局如下圖:其中包含兩個靜態(tài)文本框,兩個編輯框和兩個按紐。上面和下面位置的編輯框的ID分別為IDC_E_TEST和IDC_E_ACTUAL,通過MFC

42、 ClassWizard添加的成員變量也分別對應為DWORD m_dwTest和DWORD m_dwAct. “退出”按紐的ID為IDOK,“開始測試”按紐ID為IDC_B_TEST,用MFC ClassWizard添加此按紐的單擊消息處理函數(shù)如下:void CHightTimerDlg:OnBTest()/ TODO: Add your control notification handler code hereUpdateData(TRUE); /取輸入的測試時間值到與編輯框相關聯(lián)的成員變量m_dwTest中LARGE_INTEGER frequence;if(!QueryPerforma

43、nceFrequency( &frequence) /取高精度運行計數(shù)器的頻率,若硬件不支持則返回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; /通過頻率換算微秒數(shù)到對應的數(shù)量(與CPU時鐘有關),1秒=10000

44、00微秒ret = MySleep( test ); /調用此函數(shù)開始延時,返回實際花銷的數(shù)量m_dwAct = (DWORD)(1000000 * ret.QuadPart / frequence.QuadPart ); /換算到微秒數(shù)UpdateData(FALSE); /顯示到對話框面板其中上面調用的MySleep函數(shù)如下:LARGE_INTEGER CHightTimerDlg:MySleep(LARGE_INTEGER Interval)/ 功能:執(zhí)行實際的延時功能 / 參數(shù):Interval 參數(shù)為需要執(zhí)行的延時與時間有關的數(shù)量 / 返回值:返回此函數(shù)執(zhí)行后實際所用的時間有關的數(shù)量

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;注:別忘了在頭文件中為此函數(shù)添加函數(shù)聲明。至此,可以編譯和執(zhí)行此工程了,結果如上

46、圖所示。在本人所用的機上(奔騰366, 64M內存)測試,當測試時間超過3微秒時,準確度已經(jīng)非常高了,此時機器執(zhí)行本身延時函數(shù)代碼的時間對需要延時的時間影響很小了。上面的函數(shù)由于演示測試的需要,沒有在函數(shù)級封裝,下面給出的函數(shù)基本上可以以全局函數(shù)的形式照搬到別的程序中。BOOL MySleep(DWORD dwInterval)/ 功能:執(zhí)行微秒級的延時功能 / 參數(shù):Interval 參數(shù)為需要的延時數(shù)(單位:微秒) / 返回值:若計算機硬件不支持此功能,返回FALSE,若函數(shù)執(zhí)行成功,返回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;需要指出的是,由于在此函數(shù)中的代碼很多,機器在執(zhí)行這些代碼所花費的時間也很長,所以在需要幾個微秒的延時時,會影響精度。實際上,讀者在熟悉這種方法后,只要使用QueryPerformanceFrequency和

49、QueryPerformanceCounter這兩個函數(shù)就能按實際需要寫出自己的延時代碼了。使用CPU時間戳進行高精度計時對關注性能的程序開發(fā)人員而言,一個好的計時部件既是益友,也是良師。計時器既可以作為程序組件幫助程序員精確的控制程序進程,又是一件有力的調試武器,在有經(jīng)驗的程序員手里可以盡快的確定程序的性能瓶頸,或者對不同的算法作出有說服力的性能比較。在Windows平臺下,常用的計時器有兩種,一種是timeGetTime多媒體計時器,它可以提供毫秒級的計時。但這個精度對很多應用場合而言還是太粗糙了。另一種是QueryPerformanceCount計數(shù)器,隨系統(tǒng)的不同可以提供微秒級的計數(shù)。

50、對于實時圖形處理、多媒體數(shù)據(jù)流處理、或者實時系統(tǒng)構造的程序員,善用QueryPerformanceCount/QueryPerformanceFrequency是一項基本功。本文要介紹的,是另一種直接利用Pentium CPU內部時間戳進行計時的高精度計時手段。以下討論主要得益于Windows圖形編程一書,第15頁17頁,有興趣的讀者可以直接參考該書。關于RDTSC指令的詳細討論,可以參考Intel產(chǎn)品手冊。本文僅僅作拋磚之用。在Intel Pentium以上級別的CPU中,有一個稱為“時間戳(Time Stamp)”的部件,它以64位無符號整型數(shù)的格式,記錄了自CPU上電以來所經(jīng)過的時鐘周期

51、數(shù)。由于目前的CPU主頻都非常高,因此這個部件可以達到納秒級的計時精度。這個精確性是上述兩種方法所無法比擬的。在Pentium以上的CPU中,提供了一條機器指令RDTSC(Read Time Stamp Counter)來讀取這個時間戳的數(shù)字,并將其保存在EDX:EAX寄存器對中。由于EDX:EAX寄存器對恰好是Win32平臺下C+語言保存函數(shù)返回值的寄存器,所以我們可以把這條指令看成是一個普通的函數(shù)調用。像這樣:inline unsigned _int64 GetCycleCount() _asm RDTSC 但是不行,因為RDTSC不被C+的內嵌匯編器直接支持,所以我們要用_emit偽指令

52、直接嵌入該指令的機器碼形式0X0F、0X31,如下:inline unsigned _int64 GetCycleCount() _asm _emit 0 x0F _asm _emit 0 x31 以后在需要計數(shù)器的場合,可以像使用普通的Win32 API一樣,調用兩次GetCycleCount函數(shù),比較兩個返回值的差,像這樣: unsigned long t; t = (unsigned long)GetCycleCount(); /Do Something time-intensive . t -= (unsigned long)GetCycleCount(); Windows圖形編程第1

53、5頁編寫了一個類,把這個計數(shù)器封裝起來。有興趣的讀者可以去參考那個類的代碼。作者為了更精確的定時,做了一點小小的改進,把執(zhí)行RDTSC指令的時間,通過連續(xù)兩次調用GetCycleCount函數(shù)計算出來并保存了起來,以后每次計時結束后,都從實際得到的計數(shù)中減掉這一小段時間,以得到更準確的計時數(shù)字。但我個人覺得這一點點改進意義不大。在我的機器上實測,這條指令大概花掉了幾十到100多個周期,在Celeron 800MHz的機器上,這不過是十分之一微秒的時間。對大多數(shù)應用來說,這點時間完全可以忽略不計;而對那些確實要精確到納秒數(shù)量級的應用來說,這個補償也過于粗糙了。 這個方法的優(yōu)點是: 1.高精度???/p>

54、以直接達到納秒級的計時精度(在1GHz的CPU上每個時鐘周期就是一納秒),這是其他計時方法所難以企及的。 2.成本低。timeGetTime 函數(shù)需要鏈接多媒體庫winmm.lib,QueryPerformance* 函數(shù)根據(jù)MSDN的說明,需要硬件的支持(雖然我還沒有見過不支持的機器)和KERNEL庫的支持,所以二者都只能在Windows平臺下使用(關于DOS平臺下的高精度計時問題,可以參考圖形程序開發(fā)人員指南,里面有關于控制定時器8253的詳細說明)。但RDTSC指令是一條CPU指令,凡是i386平臺下Pentium以上的機器均支持,甚至沒有平臺的限制(我相信i386版本UNIX和Linux下這個方法同樣適用,但沒有條件試驗),而且函數(shù)調用的開銷是最小的。 3.具有和CPU主頻直接對應的速率關系。一個計數(shù)相當于1/(CPU主頻Hz數(shù))秒,這樣只要知道了CPU的主頻,可以直接計算出時間。這和

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論