C語言高手編程技巧資料_第1頁
C語言高手編程技巧資料_第2頁
C語言高手編程技巧資料_第3頁
C語言高手編程技巧資料_第4頁
C語言高手編程技巧資料_第5頁
已閱讀5頁,還剩43頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

1、C語言技巧總結(jié)自增大總結(jié):i=(i+)+(+i)i=?-    源代碼:#include <stdio.h>int main()int i = 1, j = 1, k;i = (i+) + (+i);printf("i=%d n", i);k = (j+) + (+j);printf("j=%d, k=%dn", j, k);執(zhí)行結(jié)果:i=5j=3, k=4關(guān)鍵的匯編碼片段:movl $0x1,0xfffffff0(%ebp)movl $0x1,0xfffffff4(%ebp)addl $0x1,0xffffff

2、f0(%ebp)mov 0xfffffff0(%ebp),%eaxadd %eax,0xfffffff0(%ebp)addl $0x1,0xfffffff0(%ebp)    mov 0xfffffff0(%ebp),%eaxmov %eax,0x4(%esp)movl $0x8048500,(%esp)call 80482f8 <printfplt>addl $0x1,0xfffffff4(%ebp)mov 0xfffffff4(%ebp),%eaxadd 0xfffffff4(%ebp),%eaxmov %eax,0xfffffff8(%ebp)a

3、ddl $0x1,0xfffffff4(%ebp)mov 0xfffffff8(%ebp),%eaxmov %eax,0x8(%esp)mov 0xfffffff4(%ebp),%eaxmov %eax,0x4(%esp)movl $0x8048507,(%esp)call 80482f8 <printfplt>發(fā)現(xiàn),先執(zhí)行+i,最后執(zhí)行i+。i = (i+) + (i+),i是不是等于2(0+0,接著i兩次自加)j = (i+) + (i+),j是不是等于0 (i沒自加前把值賦給了j)int,float,double之間的關(guān)聯(lián)-    其實學(xué)習(xí)過編程

4、的同學(xué),都對這三個東西再熟悉不過了。int,又稱作整型,在.net中特指的是Int32,為32位長度的有符號整型變量。 float,單精度浮點數(shù),32位長度,1位符號位,8位指數(shù)位與23位數(shù)據(jù)位,在.net中又稱為Single。double,64位長度的雙精度浮點數(shù),1位符號位,11位指數(shù)位,52位數(shù)據(jù)位。它們互相的關(guān)系就是:int可以穩(wěn)式轉(zhuǎn)換成float和double,float只能強制轉(zhuǎn)換成int,但是可以隱式轉(zhuǎn)換成double,double只能強制轉(zhuǎn)換成float和int。在說明問題之前,還很有必要溫習(xí)一下計算機組成原理時學(xué)習(xí)到的一些知識,就是二進制補碼表示以及浮點數(shù)表示。我想把一個十進

5、制轉(zhuǎn)化為二進制的方法已經(jīng)不用多費唇舌,只不過為了計算方便以及消除正零與負零的問題,現(xiàn)代計算機技術(shù),內(nèi)存里存的都是二進制的補碼形式,當(dāng)然這個也沒什么特別的,只不過有某些離散和點,需要特殊定義而已,比如-(231),這個數(shù)在int的補碼里表示成1000(31個零),這個生套補碼計算公式并不能得到結(jié)果(其實不考慮進位的話還真是這個結(jié)果,但是總讓人感覺很怪)。再者,浮點數(shù),其實就是把任何二進制數(shù)化成以0.1.開頭的科學(xué)計數(shù)法表示而已。廢話說完,這就出現(xiàn)了幾個問題,而且是比較有意思的問題。1 int i = Int32.MaxValue;2 float f = i;3 int j = (int)f;4

6、bool b = i = j;這里的b,是false。剛才這個操作,如果我們把float換成long,第一次進行隱式轉(zhuǎn)換,第二次進行強制轉(zhuǎn)換,結(jié)果將會是true。乍一看,float.MaxValue是比int.MaxValue大了不知道多少倍的,然而這個隱式轉(zhuǎn)換中,卻造成了數(shù)據(jù)丟失。 int.MaxValue,這個值等于231-1,寫成二進制補碼形式就是01111(31個1),這個數(shù),在表示成float計數(shù)的科學(xué)計數(shù)法的時候,將會寫成+0.1111(23個1)*231,對于那31個1,里面的最后8個,被float無情的拋棄了,因此,再將這個float強制轉(zhuǎn)換回 int的時候,對應(yīng)的int的二進

7、制補碼表示已經(jīng)變成了0111(23個1)00000000,這個數(shù)與最初的那個int相差了255,所以造成了不相等。那么提出另一個問題,什么樣的int變成float再變回來,和從前的值相等呢?這個問題其實完全出在那23位float的數(shù)據(jù)位上了。對于一個int,把它寫成二進制形式之后,成為了個一32個長度的0、1的排列,對于這個排列,只要第一個1與最后一個1之前的間距,不超過23,那么它轉(zhuǎn)換成 float再轉(zhuǎn)換回來,兩個值就會相等。這個問題是與大小無關(guān)的,而且這個集合在int這個全集下并不連續(xù)。1 double d = 0.6;2 float f = (float)d;3 double d2 =

8、f;4 bool b = d = d2;這里的b,也是false。剛才這個操作,如果開始另d等于0.5,結(jié)果就將會是true。乍一看,0.6這個數(shù)這么短,double和 float都肯定能夠表示,那么轉(zhuǎn)換過去再轉(zhuǎn)換回來,結(jié)果理應(yīng)相等。其實這是因為我們用十進制思考問題太久了,如果我們0.6化成二進制小數(shù),可以發(fā)現(xiàn)得到的結(jié)果是0.10011001(1001循環(huán))。這是一個無限循環(huán)小數(shù)。因此,不管float還是double,它在存儲0.6 的時候,都無法完全保存它精確的值(計算機不懂分?jǐn)?shù),呵呵),這樣的話由于float保存23位,而double保存52位,就造成了double轉(zhuǎn)化成 float的時候

9、,丟失掉了一定的數(shù)據(jù),非再轉(zhuǎn)換回去的時候,那些丟掉的值被補成了0,因此這個后來的double和從前的double值已經(jīng)不再一樣了。這樣就又產(chǎn)生了一個問題,什么樣的double轉(zhuǎn)換成float再轉(zhuǎn)換回來,兩個的值相等呢?其實這個問題與剛才int的那個問題驚人的相似(廢話,都和float打交道,能不相似么),只不過我們還需要考慮double比float多了3位的指數(shù)位,太大的數(shù)double能表示但float 不行。還有一個算是數(shù)學(xué)上的問題,什么樣的十進制小數(shù),表示成二進制不是無限小數(shù)呢?這個問題可以說完全成為數(shù)學(xué)范疇內(nèi)的問題了,但是比較簡單,答案也很明顯,對于所有的最后一位以5結(jié)尾的十進制有限小數(shù)

10、,都可以化成二進制的有限小數(shù)(雖然這個小數(shù)可能長到?jīng)]譜)。最后,一個有意思有問題,剛才說過0.6表示成為二進制小數(shù)之后,是0.1001并且以1001為循環(huán)節(jié)的無限循環(huán)小數(shù),那么在我們將它存成浮點數(shù)的時候,一定會在某個位置將它截斷(比如float的23位和double的52位),那么真正存在內(nèi)存里的這個二進制數(shù),轉(zhuǎn)化回十進制,到底是比原先的十進制數(shù)大呢,還是小呢?答案是It depends。人計算十進制的時候,是四舍五入,計算機再計算二進制小數(shù)也挺簡單,就是0舍1入。對于float,要截斷成為23位,假如卡在24位上的是1,那么就會造成進位,這樣的話,存起來的值就比真正的十進制值大了,如果是0,

11、就舍去,那么存起來的值就比真正的十進制值小了。因此,這可以合理的解釋一個問題,就是0.6d轉(zhuǎn)換成float再轉(zhuǎn)換回double,它的值是0.60000002384185791,這個值是比0.6大的,原因就是 0.6的二進制科學(xué)計數(shù)法表示,第24位是1,造成了進位。到了這里,仍然有一事不解,就是對于浮點數(shù),硬件雖然給予了計算上的支持,但是它與十進制之間的互相轉(zhuǎn)換,到底是如何做到的呢,又是誰做的呢(匯編器還是編譯器)。這個東西突出體現(xiàn)在存在內(nèi)存里的數(shù)明顯實際與0.6不等,但是無論哪種語言,都能夠在Debug以及輸入的時候,將它正確的顯示成 0.6提供給用戶(程序員),最好的例子就是double和T

12、oString方法,如果我寫double d=0.59999999999999999999999999999,d.ToString()給我的是0.6。誠然,對于double來說,我寫的那個N長的數(shù)與0.6在內(nèi)存里存的東西是一樣的,但是計算機,又如果實現(xiàn)了將一個實際與0.6不相等的數(shù)變回0.6并顯示給我的呢?關(guān)于這個問題,歡迎大家討論并請高手指教一二。從“交換兩個變量而不用臨時變量”談起-問題:寫一個函數(shù),實現(xiàn)交換兩個變量,但不能用臨時變量? 分析:交換兩個變量的這一功能,我們用的比較多,也可以很容易的寫出一個出來,但是題目要求的是不能用臨時變量。并且題目也沒有說明變量是什么類型,它是 int,

13、char,double,還是自定義的結(jié)構(gòu)體,或者是類類型,雖然可以用模板從某種程度上解決這一些問題,但對于自定義的類型,還是不是很好處理。在這里先從最簡單的說起,假設(shè)這里的變量是int型的。一般如果我們要寫一個交換整形變量的代碼是1:  inline void Swap(int *a,int *b)2:  3:       int temp;4:       temp=*a;5:       *a

14、=*b;6:       *b=*a;7:  我們用到了臨時變量temp。思考一個問題我們?yōu)槭裁匆玫脚R時變量呢?我們平常交換東西時,是否用到了臨時的媒介呢?比方說我手上有一個蘋果,你手上有一個桔子,我想和你交換,是如何進行的,用到媒介沒有呢?我們好像是直接交換的,這其中似乎沒有用到什么媒介,但是仔細想想過程是用到了媒介的。交換可以歸納為兩種方法,1:將物品直接從一個人的左手放到一個人的右手2:借助其它的容器,如果盤,先將物品放在果盤中,再去拿自己要的。其實這兩個也只是一個方法而已,第一個借助的“容器”是我們的身上的另一只空

15、閑的手,很容易忽略掉這個問題,以為我們沒有用到其它媒介,要是想想我們只有一只手如何交換呢。結(jié)論1:物質(zhì)是有載體的,不管這物質(zhì)是實體的,還是虛體。一個載體在同一時間上只能承載一種物質(zhì),于是在交換兩個載體承載的物質(zhì)時,是不可能同時進行的,估都要用到其它的媒介載體?;氐接嬎銠C的問題上,存放變量也有載體的,它是內(nèi)存,而變量可以說是一種虛體物質(zhì)。這從直觀上解釋一般為什么我們寫交換變量函數(shù)時要用到臨時變量,也直接反映了編程不是離生活很遠的,它不是另一個世界的思維。即然如此,是不是沒有辦法了?方法只給那些勤于思考的人的。是一定要用到載體媒介嗎?不用到就不行嗎?結(jié)合程序設(shè)計特點,可以有如下的代碼1: 

16、; inline void Swap(int *a,int *b)2:  3:      *a=*a+*b;4:      *b=*a-*b;5:      *a=*a-*b;6:  是不是很有意思,真的沒有用到其它的臨時變量?。∧俏覀兩厦娴慕Y(jié)論1是不是錯了呢?當(dāng)然不是,為什么可以直接就可以交換了呢?還是用上面的例子來說,兩個人交換蘋果和桔子但是每人只有一個手,也不借助其它的容器,那如何進行喲?有辦法,就只是用一個手拿兩樣?xùn)|西

17、,也就是說先將蘋果(桔子)給另一個人用一只手拿著,再從他手上拿桔子(蘋果)?;疽簿褪谴a的意思了。結(jié)論2:看似不可能,但卻可以的,要做的是仔細的去挖掘,多思考。那上面的代碼是不是就沒有問題了?有。要考慮溢出的問題,程序中有一個加法,很可能使結(jié)果超出范圍,也就是說一只手要是拿不了一個蘋果和桔子怎樣辦?要相信有問題是可以解決的,可以用assert等等方面處理一下,但不是最好,繼續(xù)有如下的代碼1:  inline void Swap(int *a, int *b)2:  3:     *a = *b;4:   

18、;  *b = *a;5:     *a = *b;6:  這是什么?一眼看不出是什么是嗎?不慌,我們可以慢慢的一步步的分析。對于位運算與、或、非,我們在處理數(shù)據(jù)的時候是經(jīng)常要用到的。我們經(jīng)常要用到的東西也是在基礎(chǔ)的。設(shè)uBitTest為測試數(shù),值為2n,也就是第n位為1。uDate為需要處理的數(shù)據(jù)。1.將uDate的第n位置1uDate = uDate|uBitTest2.將uDate的第n位置0uDate = uDate&uBitTest3.將uDate的第n位取反uDate = uDate uBitTest掌握熟悉了這些

19、在看上面的代碼是不是要輕松多了,不是要溢出嗎?不是一只手拿不下蘋果和桔子嗎?不要緊,我們可以一部分的交換啊,把蘋果切開,桔子剝開來去交換,這樣總可以拿下來吧。結(jié)論3:載體承載物質(zhì)是必需的,我們不能以改變,卻可以改變載體承載的性質(zhì)。載體的容量大小不能改變,但可以去改變承載物體的大小,照樣可以達到想要的目的。真的是山窮水復(fù)疑無路,柳暗花明又一村。到這里為止,對于簡單的整形變量的交換的情況,已經(jīng)基本上說清楚了,但并不是說可以去支持任何類型,在這里只是從最簡單的例子開始,只是起個引子的作用,是給一些思路的問題。對于這個問題,還可以從內(nèi)聯(lián)匯編的方面去處理,可以看看匯編的代碼是如何,從中得到一些啟發(fā);對于

20、各種不同類型處理也有其各自的問題需要去處理,如對于類對象怎樣去處理拷貝。對于好的庫也可以去看看他們是怎樣去寫swap這個函數(shù)的。如stl,boost等,從中應(yīng)該可以學(xué)到不少的。巧用C的Time函數(shù)進行時間轉(zhuǎn)換-我們經(jīng)常要遇到時間處理的問題,比如要開發(fā)一個schedule的功能,或根據(jù)修改時間來過濾文件等。windows API提供了Get*Time()系列函數(shù)用于獲取當(dāng)前時間,但是沒有提供進行時間轉(zhuǎn)換的,比如我們要得到距離當(dāng)前時間2年4個月5天的時間,我們就得自己去計算了。但是這里有個問題,如果被減的天數(shù)大于當(dāng)前月份的天數(shù),那么天數(shù)就會變成負值。為了解決這個問題,我們就根據(jù)不同月份的天數(shù)來計算

21、偏移,同時做月和年的變化。不過這種方法很麻煩,因為每個月天數(shù)是不同的還需要考慮閏年和平年的問題。其實C的Time系列函數(shù)可以很好的解決這個問題, 1. 首先用TM結(jié)構(gòu)進行需要的時間偏移2. 然后利用mktime這個函數(shù)將TM結(jié)構(gòu)轉(zhuǎn)換到從1900.1.1開始的秒數(shù)值3. 再利用localtime 把秒數(shù)轉(zhuǎn)換成TM結(jié)構(gòu)示例代碼如下:代碼#include "stdafx.h"#include <Windows.h>#include <time.h>#include <iostream>using namespace std;void Offse

22、tDateTime(const struct tm* inST, struct tm* outST,int dYears, int dMonths, int dDays,int dHours, int dMinutes, int dSeconds)if (inST != NULL && outST != NULL)/ 偏移當(dāng)前時間outST->tm_year = inST->tm_year - dYears;outST->tm_mon  = inST->tm_mon  - dMonths;outST->tm_mday = inS

23、T->tm_mday - dDays;outST->tm_hour = inST->tm_hour - dHours;outST->tm_min  = inST->tm_min  - dMinutes;outST->tm_sec  = inST->tm_sec  - dSeconds;/ 轉(zhuǎn)換到從1900.1.1開始的總秒數(shù)time_t newRawTime = mktime(outST);/ 將秒數(shù)轉(zhuǎn)換成時間結(jié)構(gòu)體outST = localtime(&newRawTime);int _tmain(int

24、 argc, _TCHAR* argv)time_t rawtime;struct tm * st;/ 獲取本地當(dāng)前時間time(&rawtime);st = localtime(&rawtime);cout << st->tm_year << "-" << st->tm_mon << "-" << st->tm_mday << endl;/ 計算時間偏移struct tm outst;OffsetDateTime(st, &outst, 2

25、, 3, 20, 0, 0, 0);time_t newTime = mktime(&outst);cout << outst.tm_year << "-" << outst.tm_mon << "-" << outst.tm_mday << endl;cout << "rawTime: " << rawtime << endl << "newTime :" << newTim

26、e << endl;return 0;C語言中標(biāo)準(zhǔn)輸入流、標(biāo)準(zhǔn)輸出流、標(biāo)準(zhǔn)錯誤輸出流-在Linux中,所有對設(shè)備和文件的操作都使用文件描述符來進行。 Linux中一個進程啟動時,都會打開3個文件:標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)出錯處理。這三個文件分別對應(yīng)文件描述符0、1、2。在C語言中,在程序開始運行時,系統(tǒng)自動打開3個標(biāo)準(zhǔn)文件:標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)出錯輸出。通常這3個文件都與終端相聯(lián)系。因此,以前我們所用到的從終端輸入或輸出都不需要打開終端文件。系統(tǒng)自定義了3個文件指針 stdin、stdout、stderr,分別指向終端輸入、終端輸出和標(biāo)準(zhǔn)出錯輸出(也從終端輸出)。標(biāo)準(zhǔn)輸入流:s

27、tdin標(biāo)準(zhǔn)輸出流:stdout標(biāo)準(zhǔn)錯誤輸出流:stderrstdinobject<cstdio>FILE * stdin;Standard input streamThe standard input stream is the default source of data for applications. It is usually directed to the input device of the standard console (generally, a keyboard).stdin can be used as an argument for any funct

28、ion that expects an input stream as one of its parameters, like fgets or fscanf.Although it is generally safe to assume that the source of data for stdin is going to be a keyboard, bear in mind that this may not be the case even in regular console systems, since stdin can be redirected at the operat

29、ing system level. For example, many systems, among them DOS/Windows and most UNIX shells, support the following command syntax:myapplication < example.txtto use the content of the file example.txt as the primary source of data for myapplication instead of the console keyboard.It is also possible

30、to redirect stdin to some other source of data from within a program using the freopen function.stdoutobject<cstdio>FILE * stdout;Standard output streamThe standard output stream is the default destination of regular output for applications. It is usually directed to the output device of the s

31、tandard console (generally, the screen).stdout can be used as an argument for any function that expects an output stream as one of its parameters, like fputs or fprintf.Although it is generally safe to assume that the default destination for stdout is going to be the screen, bear in mind that this m

32、ay not be the case even in regular console systems, since stdout can be redirected at the operating system level. For example, many systems, among them DOS/Windows and most UNIX shells, support the following command syntax:myapplication > example.txtto redirect the output of myapplication to the

33、file example.txt instead of the screen.It is also possible to redirect stdout to some other source of data from within a program using the freopen function.stderrobject<cstdio>FILE * stderr;Standard error streamThe standard error stream is the default destination for error messages and other d

34、iagnostic warnings. Like stdout, it is usually also directed to the output device of the standard console (generally, the screen).stderr can be used as an argument for any function that expects an output stream as one of its parameters, like fputs or fprintf.Although generally both stdout and stderr

35、 are associated with the same console output, applications may differentiate between what is sent to stdout and what to stderrfor the case that one of them is redirected. For example, it is frequent to redirect the regular output of a console program (stdout) to a file while expecting the error mess

36、ages to keep appearing in the console screen.It is also possible to redirect stderr to some other destination from within a program using the freopen function.perrorfunction<cstdio>void perror ( const char * str );Print error messageInterprets the value of the global variable errno into a stri

37、ng and prints that string to stderr (standard error output stream, usually the screen), optionaly preceding it with the custom message specified in str.errno is an integral variable whose value describes the last error produced by a call to a library function. The error strings produced by perror de

38、pend on the developing platform and compiler.If the parameter str is not a null pointer, str is printed followed by a colon (:) and a space. Then, whether str was a null pointer or not, the generated error description is printed followed by a newline character (''n'').perror should b

39、e called right after the error was produced, otherwise it can be overwritten in calls to other functions.Parameters.strC string containing a custom message to be printed before the error message itself.If it is a null pointer, no preceding custom message is printed, but the error message is printed

40、anyway.By convention, the name of the application itself is generally used as parameter.獲取當(dāng)前系統(tǒng)所有進程-#include "stdafx.h" #include <windows.h>#include <tlhelp32.h>int main(int argc, char* argv)HANDLE hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);if (!hSnapshot)print

41、f("CreateToolhelp32Snapshot ERROR!n");return 1;PROCESSENTRY32 pe32;pe32.dwSize = sizeof(PROCESSENTRY32 );if (!Process32First (hSnapshot, &pe32)printf("Process32First ERROR!n");doprintf("ProcID:%d-%sn",pe32.th32ProcessID ,pe32.szExeFile );while(Process32Next (hSnapsh

42、ot, &pe32);return 0;有趣的位運算-今天碰到一個問題:在不增加新的變量的情況下,交換兩個int型變量的值。題目描述很簡單,但是考慮起來還是比較有難度的。這里提出一個位運算的妙用:異或的妙用 異或是這樣一種運算:如果兩位相同,同為1或同為0,那么異或為0,否則異或為1.同時需要說明的是,在進行異或運算的是補碼。知道規(guī)則以后,我們可以來看這個問題了。這個題目可以這樣做:假設(shè)兩個int型變量a,b: int a, b;a = a b;b = a b;a = a b;hoho!3次同樣的操作,完成了這樣一個神奇的運算,位運算大有可為。C中如何顯示*.bmp文件-1.*.bmp

43、文件結(jié)構(gòu) *.bmp文件和大多數(shù)圖形文件一樣,分為文件描述區(qū)(頭文件信息)和圖象存儲區(qū)(象素數(shù)據(jù))兩部分。而頭文件信息中又包含了信息區(qū)和調(diào)色板區(qū)兩部分,信息區(qū)又可以細分為文件信息區(qū)和圖象信息區(qū)兩部分。這里以256色320*200的bmp圖象為例。頭文件描述區(qū)的偏移長度是1078個字節(jié),也就是說圖象存儲區(qū)是從文件偏移1078后開始讀取的。在頭文件描述區(qū)中頭信息區(qū)的偏移長度是54個字節(jié),也就是說調(diào)色板數(shù)據(jù)區(qū)是從54-1078之間的1024字節(jié)。在頭信息區(qū)中文件信息區(qū)占14個字節(jié)而圖象信息區(qū)占40字節(jié)。(1) 文件信息區(qū)typedef struct BMP_fileunsigned int bfTy

44、pe; /文件類型unsigned long bfSize; /bmp文件長度unsigned int Reserved1;unsigned int Reserved2;unsigned long bfOffset; /文件描述區(qū)長度,16色為118,256色為1078bitmapfile;現(xiàn)在算一下,有3個int,2個long,正好3*2+2*4=14字節(jié)(2) 圖象信息區(qū)type struct BMP_infounsigned long biSize;unsigned long biWidth;unsigned long biHeight;unsigned int biPlanes;uns

45、igned int biBitCount;unsigned long biCompression;unsigned long biSizeImage;unsigned long biXplosPerMeter;unsigned long biYplosPerMeter;unsigned long biClrUsed;unsigned long biClrImportant;bitmapinfo;現(xiàn)在算一下,2個int,9個long,正好是2+2*9*4=40字節(jié)。(3)調(diào)色板區(qū)typedef struct RGB_BMP_typunsigned char blue;unsigned char

46、green;unsigned char red;unsigned char reserved;RGB_BMP,*RGB_BMP_ptr;說明:三原色+灰度,共4*256=1024字節(jié)。下面是bmp文件的完整的結(jié)構(gòu)定義:typedef struct bmp_picture_typbitmapfile file;bitmapinfo info;RGB_BMP palette256;char far *buffer;bmp_picture, *bmp_picture_ptr;2. bmp文件的顯示(1)圖象存儲區(qū)的讀取由于bmp圖象是從下至上存儲的,所以我們不能進行直接順序讀取。詳細的說,bmp圖象

47、存儲區(qū)數(shù)據(jù)是從1078偏移字節(jié)開始。文件內(nèi)第一個圖象點實際上是對應(yīng)圖象(320*200)第200行的最左邊的第一個點,而從1078開始的320個點則是圖象最下面一行對應(yīng)的點,之后的321個點是圖象倒數(shù)第二行最左邊的第一個點。這樣,bmp文件最后一個字節(jié)對應(yīng)的點是第一行最后邊的點了。下面是實現(xiàn)bmp文件圖象存儲區(qū)數(shù)據(jù)讀取到內(nèi)存的代碼:for(i=info.biHeight-1;i>=0;i-)lseek(fp,1078+(long)(info.biHeight-i-1)*info.biWidth,0);read(fp,&bmp256->bufferi*info.biWidth

48、,info.biWidth);(2)調(diào)色板的讀取除了圖象存儲區(qū)的存放規(guī)則是倒序的以外,bmp文件調(diào)色板內(nèi)容也是以B,G,R,灰度的順序存放的。所以讀取時不要將文件中的三原色中的蘭色對應(yīng)給調(diào)色板結(jié)構(gòu)體變量中的紅色。同時,由于三原色只使用64種(6位)色階,而三原色的存放空間是1字節(jié)(8位),所以要將bmp文件三原色的6位數(shù)據(jù)都放在1個字節(jié)的高位,則在讀取調(diào)色板結(jié)構(gòu)體變量的時候必須進行右移2位。以下給出讀取文件調(diào)色板數(shù)據(jù)的代碼for(i=0;i<256;i+)read(fp,&bmp256->palette.blue,1);read(fp,&bmp256->pal

49、ette.green,1);read(fp,&bmp256->palette.red,1);read(fp,&bmp256->palette.reserved,1);bmp256->palette.blue=bmp256->palette.blue>>2;bmp256->palette.green=bmp256->palette.green>>2;bmp256->palette.red=bmp256->palette.red>>2;以下給出寫入調(diào)色板函數(shù)void Set_BMP_Palette_

50、Register(int index,RGB_BMP_ptr color)outp(PALETTE_MASK,0XFF);outp(PALETTE_REGISTER_WR,index); /*確定調(diào)色板序號*/outp(PALETTE_DATA,color->red); /*設(shè)置該序號為紅色*/outp(PALETTE_DATA,color->green); /*設(shè)置該序號為綠色*/outp(PALETTE_DATA,color->blue); /*設(shè)置該序號為蘭色*/調(diào)用調(diào)色板的代碼:for(i=0;i<256,i+)Set_BMP_Palette_Register(

51、i,(RGB_BMP_ptr) &bmp256->palette);現(xiàn)在我們開始進行完整的讀取(1)申請內(nèi)存空間(2)檢查頭文件信息區(qū)(3)讀取調(diào)色板數(shù)據(jù)(4)讀取位圖到內(nèi)存(5)顯示圖象(6)內(nèi)存釋放(1)申請內(nèi)存:由于320*200是64K,而C程序允許用戶申請的內(nèi)存空間也只有64K,為了防止一次申請不到,我們可以分幾次申請。使用malloc()函數(shù)就可以。(2)檢查頭文件信息區(qū):A:判斷是不是bmp文件(若不是,則無法顯示)B:文件是否為壓縮格式(若是,則無法處理)C:文件是否為256色以下給出讀取頭文件信息到內(nèi)存的代碼:read(fp,&bmp256->fi

52、le,sizeof(bitmapfile);read(fp,&bmp256->info,sizeof(bitmapinfo);以下是檢測bmp文件格式的函數(shù):void Check_Bmp(bmp_picture_ptr bmp_ptr)if(bmp_ptr->file.bfType!=0x4d42) /*檢測是不是bmp文件*/printf("Not a BMP file! ");exit(1);if(bmp_ptr->info.biCompression!=0) /*檢測是不是壓縮文件,1表示壓縮,0表示沒壓縮*/printf("Can

53、 not display a compressed bmp file! ");exit(1);if(bmp_ptr->info.biBitCount!=8) /*檢測是不是256色*/printf("Not a index 256color bmp file! ");exit(1);(3)和(4)前面有介紹,這兒省略。(5)顯示圖象假設(shè)bmp文件的調(diào)色板區(qū)已寫入計算機調(diào)色板,bmp文件圖象存儲區(qū)也已寫到內(nèi)存,以下給出具體的顯示到屏幕的函數(shù)void BMP_Show_Buffer2(bmp_picture_ptr image)memcpy(char far *

54、)video_buffer,(char far *)image->buffer,(unsigned int)info->biWidth*info->biHight/2)(6)釋放內(nèi)存void BMP_Delete(bmp_picture_ptr image)free(image->buffer);好了,經(jīng)過以上幾步,基本上你的bmp文件就能在C中顯示了。當(dāng)然前提是你使用256色的圖形驅(qū)動。也就是你的int gdrive=DETECT應(yīng)該改為int gdrive=6;如果你僅僅顯示16色的文件,就沒有必要了。用extern聲明外部變量-全局變量(外部變量)是在函數(shù)的外部定

55、義的,它的作用域為從變量的定義處開始,到本程序文件的末尾。在此作用域內(nèi),全局變量可以為本文件中各個函數(shù)所引用。編譯時將全局變量分配在靜態(tài)存儲區(qū)。 有時需要用extern來聲明全局變量,以擴展全局變量的作用域。1. 在一個文件內(nèi)聲明全局變量如果外部變量不在文件的開頭定義,其有效的作用范圍只限于定義處到文件終了。如果在定義點之前的函數(shù)想引用該全局變量,則應(yīng)該在引用之前用關(guān)鍵字extern對該變量作外部變量聲明,表示該變量是一個將在下面定義的全局變量。有了此聲明,就可以從聲明處起,合法地引用該全局變量,這種聲明稱為提前引用聲明。例4.14 用extern對外部變量作提前引用聲明,以擴展程序文件中的作

56、用域。#include <iostream>using namespace std;int max(int,int);              /函數(shù)聲明void main( )extern int a,b;               /對全局變量a,b作提前引用聲明cout<<max(a,b)<

57、;<endl;int a=15,b=-7;                  /定義全局變量a,bint max(int x,int y)int z;z=x>y?x:y;return z;運行結(jié)果如下:15在main后面定義了全局變量a,b,但由于全局變量定義的位置在函數(shù)main之后,因此如果沒有程序的第5行,在main函數(shù)中是不能引用全局變量a和b的?,F(xiàn)在我們在main函數(shù)第2行用extern對a和b作了提前引用

58、聲明,表示a和b是將在后面定義的變量。這樣在main函數(shù)中就可以合法地使用全局變量a和b了。如果不作extern聲明,編譯時會出錯,系統(tǒng)認(rèn)為a和b未經(jīng)定義。一般都把全局變量的定義放在引用它的所有函數(shù)之前,這樣可以避免在函數(shù)中多加一個extern聲明。2. 在多文件的程序中聲明外部變量如果一個程序包含兩個文件,在兩個文件中都要用到同一個外部變量num,不能分別在兩個文件中各自定義一個外部變量num。正確的做法是:在任一個文件中定義外部變量num,而在另一文件中用extern對num作外部變量聲明。即extern int num;編譯系統(tǒng)由此知道num是一個已在別處定義的外部變量,它先在本文件中找

59、有無外部變量num,如果有,則將其作用域擴展到本行開始(如上節(jié)所述),如果本文件中無此外部變量,則在程序連接時從其他文件中找有無外部變量num,如果有,則把在另一文件中定義的外部變量num的作用域擴展到本文件,在本文件中可以合法地引用該外部變量num。分析下例:file1.cpp                          

60、0;                  file2.cppextern int a,b;                                 

溫馨提示

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

評論

0/150

提交評論