![C語言難點筆記_第1頁](http://file4.renrendoc.com/view6/M00/3E/0C/wKhkGWejBIeAcps1AAEwHyBMd3I816.jpg)
![C語言難點筆記_第2頁](http://file4.renrendoc.com/view6/M00/3E/0C/wKhkGWejBIeAcps1AAEwHyBMd3I8162.jpg)
![C語言難點筆記_第3頁](http://file4.renrendoc.com/view6/M00/3E/0C/wKhkGWejBIeAcps1AAEwHyBMd3I8163.jpg)
![C語言難點筆記_第4頁](http://file4.renrendoc.com/view6/M00/3E/0C/wKhkGWejBIeAcps1AAEwHyBMd3I8164.jpg)
![C語言難點筆記_第5頁](http://file4.renrendoc.com/view6/M00/3E/0C/wKhkGWejBIeAcps1AAEwHyBMd3I8165.jpg)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
C語言深入學(xué)習(xí)知識點目錄1. C語言中的指針和內(nèi)存泄漏 52. C語言難點分析整理 103. C語言難點 184. C/C++實現(xiàn)冒泡排序算法 325. C++中指針和引用的區(qū)別 356. constchar*,charconst*,char*const的區(qū)別 367. C中可變參數(shù)函數(shù)實現(xiàn) 388. C程序內(nèi)存中組成部分 419. C編程拾粹 4210. C語言中實現(xiàn)數(shù)組的動態(tài)增長 4411. C語言中的位運算 4612. 浮點數(shù)的存儲格式: 5013. 位域 5814. C語言函數(shù)二維數(shù)組傳遞方法 6415. C語言復(fù)雜表達(dá)式的執(zhí)行步驟 6616. C語言字符串函數(shù)大全 6817. C語言宏定義技巧 8918. C語言實現(xiàn)動態(tài)數(shù)組 10019. C語言筆試-運算符和表達(dá)式 10420. C語言編程準(zhǔn)則之穩(wěn)定篇 10721. C語言編程常見問題分析 10822. C語言編程易犯毛病集合 11223. C語言缺陷與陷阱(筆記) 11924. C語言防止緩沖區(qū)溢出方法 12625. C語言高效編程秘籍 12826. C運算符優(yōu)先級口訣 13327. do/while(0)的妙用 13428. exit()和return()的區(qū)別 14029. exit子程序終止函數(shù)與return的差別 14130. extern與static存儲空間矛盾 14531. PC-Lint與C\C++代碼質(zhì)量 14732. spirntf函數(shù)使用大全 15833. 二叉樹的數(shù)據(jù)結(jié)構(gòu) 16734. 位運算應(yīng)用口訣和實例 17035. 內(nèi)存對齊與ANSIC中struct內(nèi)存布局 17336. 冒泡和選擇排序?qū)崿F(xiàn) 18037. 函數(shù)指針數(shù)組與返回數(shù)組指針的函數(shù) 18638. 右左法則-復(fù)雜指針解析 18939. 回車和換行的區(qū)別 19240. 堆和堆棧的區(qū)別 19441. 堆和堆棧的區(qū)別 19842. 如何寫出專業(yè)的C頭文件 20243. 打造最快的Hash表 20744. 指針與數(shù)組學(xué)習(xí)筆記 22245. 數(shù)組不是指針 22446. 標(biāo)準(zhǔn)C中字符串分割的方法 22847. 漢諾塔源碼 23148. 洗牌算法 23449. 深入理解C語言指針的奧秘 23650. 游戲外掛的編寫原理 25451. 程序?qū)嵗治?為什么會陷入死循環(huán) 25852. 空指針究竟指向了內(nèi)存的哪個地方 26053. 算術(shù)表達(dá)式的計算 26554. 結(jié)構(gòu)體對齊的具體含義 26955. 連連看AI算法 27456. 連連看尋路算法的思路 28357. 重新認(rèn)識:指向函數(shù)的指針 28858. 鏈表的源碼 29159. 高質(zhì)量的子程序 29560. 高級C語言程序員測試必過的十六道最佳題目+答案詳解 29761. C語言常見錯誤 32062. 超強的指針學(xué)習(xí)筆記 32563. 程序員之路──關(guān)于代碼風(fēng)格 34364. 指針、結(jié)構(gòu)體、聯(lián)合體的安全規(guī)范 34665. C指針講解 35266. 關(guān)于指向指針的指針 36867. C/C++誤區(qū)一:voidmain() 37368. C/C++誤區(qū)二:fflush(stdin) 37669. C/C++誤區(qū)三:強制轉(zhuǎn)換malloc()的返回值 38070. C/C++誤區(qū)四:charc=getchar(); 38171. C/C++誤區(qū)五:檢查new的返回值 38372. C是C++的子集嗎? 38473. C和C++的區(qū)別是什么? 38774. 無條件循環(huán) 38875. 產(chǎn)生隨機數(shù)的方法 38976. 順序表及其操作 39077. 單鏈表的實現(xiàn)及其操作 39178. 雙向鏈表 39579. 程序員數(shù)據(jù)結(jié)構(gòu)筆記 39980. Hashtable和HashMap的區(qū)別 40881. hash表學(xué)習(xí)筆記 41082. C程序設(shè)計常用算法源代碼 41283. C語言有頭結(jié)點鏈表的經(jīng)典實現(xiàn) 41984. C語言惠通面試題 42885. C語言常用宏定義 450
1. C語言中的指針和內(nèi)存泄漏在使用C語言時,您是否對花時間調(diào)試指針和內(nèi)存泄漏問題感到厭倦?如果是這樣,那么本文就適合您。您將了解可能導(dǎo)致內(nèi)存破壞的指針操作類型,您還將研究一些場景,了解要在使用動態(tài)內(nèi)存分配時考慮什么問題。引言對于任何使用C語言的人,如果問他們C語言的最大煩惱是什么,其中許多人可能會回答說是指針和內(nèi)存泄漏。這些的確是消耗了開發(fā)人員大多數(shù)調(diào)試時間的事項。指針和內(nèi)存泄漏對某些開發(fā)人員來說似乎令人畏懼,但是一旦您了解了指針及其關(guān)聯(lián)內(nèi)存操作的基礎(chǔ),它們就是您在C語言中擁有的最強大工具。本文將與您分享開發(fā)人員在開始使用指針來編程前應(yīng)該知道的秘密。本文內(nèi)容包括:1. 導(dǎo)致內(nèi)存破壞的指針操作類型2. 在使用動態(tài)內(nèi)存分配時必須考慮的檢查點3. 導(dǎo)致內(nèi)存泄漏的場景如果您預(yù)先知道什么地方可能出錯,那么您就能夠小心避免陷阱,并消除大多數(shù)與指針和內(nèi)存相關(guān)的問題。? 什么地方可能出錯?有幾種問題場景可能會出現(xiàn),從而可能在完成生成后導(dǎo)致問題。在處理指針時,您可以使用本文中的信息來避免許多問題。1. 未初始化的內(nèi)存在本例中,p已被分配了10個字節(jié)。這10個字節(jié)可能包含垃圾數(shù)據(jù),如圖1所示。char*p=malloc(10);圖1.垃圾數(shù)據(jù)如果在對這個p賦值前,某個代碼段嘗試訪問它,則可能會獲得垃圾值,您的程序可能具有不可預(yù)測的行為。p可能具有您的程序從未曾預(yù)料到的值。良好的實踐是始終結(jié)合使用memset和malloc,或者使用calloc。char*p=malloc(10);memset(p,’\0’,10);現(xiàn)在,即使同一個代碼段嘗試在對p賦值前訪問它,該代碼段也能正確處理Null值(在理想情況下應(yīng)具有的值),然后將具有正確的行為。2. 內(nèi)存覆蓋由于p已被分配了10個字節(jié),如果某個代碼片段嘗試向p寫入一個11字節(jié)的值,則該操作將在不告訴您的情況下自動從其他某個位置“吃掉”一個字節(jié)。讓我們假設(shè)指針q表示該內(nèi)存。圖2.原始q內(nèi)容圖3.覆蓋后的q內(nèi)容結(jié)果,指針q將具有從未預(yù)料到的內(nèi)容。即使您的模塊編碼得足夠好,也可能由于某個共存模塊執(zhí)行某些內(nèi)存操作而具有不正確的行為。下面的示例代碼片段也可以說明這種場景。Char*name=(char*)malloc(11);//Assignsomevaluetonamememcpy(p,name,11);//Problembeginshere在本例中,memcpy操作嘗試將11個字節(jié)寫到p,而后者僅被分配了10個字節(jié)。作為良好的實踐,每當(dāng)向指針寫入值時,都要確保對可用字節(jié)數(shù)和所寫入的字節(jié)數(shù)進行交叉核對。一般情況下,memcpy函數(shù)將是用于此目的的檢查點。內(nèi)存讀取越界內(nèi)存讀取越界(overread)是指所讀取的字節(jié)數(shù)多于它們應(yīng)有的字節(jié)數(shù)。這個問題并不太嚴(yán)重,在此就不再詳述了。下面的代碼提供了一個示例。char*ptr=(char*)malloc(10);charname[20];memcpy(name,ptr,20);//Problembeginshere在本例中,memcpy操作嘗試從ptr讀取20個字節(jié),但是后者僅被分配了10個字節(jié)。這還會導(dǎo)致不希望的輸出。內(nèi)存泄漏內(nèi)存泄漏可能真正令人討厭。下面的列表描述了一些導(dǎo)致內(nèi)存泄漏的場景。重新賦值我將使用一個示例來說明重新賦值問題。char*memoryArea=malloc(10);char*newArea=malloc(10);這向如下面的圖4所示的內(nèi)存位置賦值。圖4.內(nèi)存位置memoryArea和newArea分別被分配了10個字節(jié),它們各自的內(nèi)容如圖4所示。如果某人執(zhí)行如下所示的語句(指針重新賦值)……memoryArea=newArea;則它肯定會在該模塊開發(fā)的后續(xù)階段給您帶來麻煩。在上面的代碼語句中,開發(fā)人員將memoryArea指針賦值給newArea指針。結(jié)果,memoryArea以前所指向的內(nèi)存位置變成了孤立的,如下面的圖5所示。它無法釋放,因為沒有指向該位置的引用。這會導(dǎo)致10個字節(jié)的內(nèi)存泄漏。圖5.內(nèi)存泄漏在對指針賦值前,請確保內(nèi)存位置不會變?yōu)楣铝⒌?。首先釋放父塊假設(shè)有一個指針memoryArea,它指向一個10字節(jié)的內(nèi)存位置。該內(nèi)存位置的第三個字節(jié)又指向某個動態(tài)分配的10字節(jié)的內(nèi)存位置,如圖6所示。圖6.動態(tài)分配的內(nèi)存free(memoryArea)如果通過調(diào)用free來釋放了memoryArea,則newArea指針也會因此而變得無效。newArea以前所指向的內(nèi)存位置無法釋放,因為已經(jīng)沒有指向該位置的指針。換句話說,newArea所指向的內(nèi)存位置變?yōu)榱斯铝⒌模瑥亩鴮?dǎo)致了內(nèi)存泄漏。每當(dāng)釋放結(jié)構(gòu)化的元素,而該元素又包含指向動態(tài)分配的內(nèi)存位置的指針時,應(yīng)首先遍歷子內(nèi)存位置(在此例中為newArea),并從那里開始釋放,然后再遍歷回父節(jié)點。這里的正確實現(xiàn)應(yīng)該為:free(memoryArea->newArea);free(memoryArea);返回值的不正確處理有時,某些函數(shù)會返回對動態(tài)分配的內(nèi)存的引用。跟蹤該內(nèi)存位置并正確地處理它就成為了calling函數(shù)的職責(zé)。char*func(){returnmalloc(20);//makesuretomemsetthislocationto‘\0’…}voidcallingFunc(){func();//Problemlieshere}在上面的示例中,callingFunc()函數(shù)中對func()函數(shù)的調(diào)用未處理該內(nèi)存位置的返回地址。結(jié)果,func()函數(shù)所分配的20個字節(jié)的塊就丟失了,并導(dǎo)致了內(nèi)存泄漏。歸還您所獲得的在開發(fā)組件時,可能存在大量的動態(tài)內(nèi)存分配。您可能會忘了跟蹤所有指針(指向這些內(nèi)存位置),并且某些內(nèi)存段沒有釋放,還保持分配給該程序。始終要跟蹤所有內(nèi)存分配,并在任何適當(dāng)?shù)臅r候釋放它們。事實上,可以開發(fā)某種機制來跟蹤這些分配,比如在鏈表節(jié)點本身中保留一個計數(shù)器(但您還必須考慮該機制的額外開銷)。訪問空指針訪問空指針是非常危險的,因為它可能使您的程序崩潰。始終要確保您不是在訪問空指針??偨Y(jié)本文討論了幾種在使用動態(tài)內(nèi)存分配時可以避免的陷阱。要避免內(nèi)存相關(guān)的問題,良好的實踐是:始終結(jié)合使用memset和malloc,或始終使用calloc。每當(dāng)向指針寫入值時,都要確保對可用字節(jié)數(shù)和所寫入的字節(jié)數(shù)進行交叉核對。在對指針賦值前,要確保沒有內(nèi)存位置會變?yōu)楣铝⒌摹C慨?dāng)釋放結(jié)構(gòu)化的元素(而該元素又包含指向動態(tài)分配的內(nèi)存位置的指針)時,都應(yīng)首先遍歷子內(nèi)存位置并從那里開始釋放,然后再遍歷回父節(jié)點。始終正確處理返回動態(tài)分配的內(nèi)存引用的函數(shù)返回值。每個malloc都要有一個對應(yīng)的free。確保您不是在訪問空指針。2. C語言難點分析整理這篇文章主要是介紹一些在復(fù)習(xí)C語言的過程中筆者個人認(rèn)為比較重點的地方,較好的掌握這些重點會使對C的運用更加得心應(yīng)手。此外會包括一些細(xì)節(jié)、易錯的地方。涉及的主要內(nèi)容包括:變量的作用域和存儲類別、函數(shù)、數(shù)組、字符串、指針、文件、鏈表等。一些最基本的概念在此就不多作解釋了,僅希望能有只言片語給同是C語言初學(xué)者的學(xué)習(xí)和上機過程提供一點點的幫助。變量作用域和存儲類別:了解了基本的變量類型后,我們要進一步了解它的存儲類別和變量作用域問題。變量類別 子類別局部變量 靜態(tài)變量(離開函數(shù),變量值仍保留) 自動變量 寄存器變量全局變量 靜態(tài)變量(只能在本文件中用) 非靜態(tài)變量(允許其他文件使用)換一個角度變量類別 子類別靜態(tài)存儲變量 靜態(tài)局部變量(函數(shù)) 靜態(tài)全局變量(本文件) 非靜態(tài)全局/外部變量(其他文件引用)動態(tài)存儲變量 自動變量 寄存器變量 形式參數(shù)extern型的存儲變量在處理多文件問題時常能用到,在一個文件中定義extern型的變量即說明這個變量用的是其他文件的。順便說一下,筆者在做課設(shè)時遇到outofmemory的錯誤,于是改成做多文件,再把它include進來(注意自己寫的*.h要用“”不用<>),能起到一定的效用。static型的在讀程序?qū)懡Y(jié)果的試題中是個考點。多數(shù)時候整個程序會出現(xiàn)多個定義的變量在不同的函數(shù)中,考查在不同位置同一變量的值是多少。主要是遵循一個原則,只要本函數(shù)內(nèi)沒有定義的變量就用全局變量(而不是main里的),全局變量和局部變量重名時局部變量起作用,當(dāng)然還要注意靜態(tài)與自動變量的區(qū)別。函數(shù):對于函數(shù)最基本的理解是從那個叫main的單詞開始的,一開始總會覺得把語句一并寫在main里不是挺好的么,為什么偏擇出去。其實這是因為對函數(shù)還不夠熟練,否則函數(shù)的運用會給我們編程帶來極大的便利。我們要知道函數(shù)的返回值類型,參數(shù)的類型,以及調(diào)用函數(shù)時的形式。事先的函數(shù)說明也能起到一個提醒的好作用。所謂形參和實參,即在調(diào)用函數(shù)時寫在括號里的就是實參,函數(shù)本身用的就是形參,在畫流程圖時用平行四邊形表示傳參。函數(shù)的另一個應(yīng)用例子就是遞歸了,筆者開始比較頭疼的問題,反應(yīng)總是比較遲鈍,按照老師的方法,把遞歸的過程耐心準(zhǔn)確的逐級畫出來,學(xué)習(xí)的效果還是比較好的,會覺得這種遞歸的運用是挺巧的,事實上,著名的八皇后、漢諾塔等問題都用到了遞歸。例子:longfun(intn){longs;if(n==1||n==2)s=2;elses=n-fun(n-1);returns;}main(){printf("%ld",fun(4));}數(shù)組:分為一維數(shù)組和多維數(shù)組,其存儲方式畫為表格的話就會一目了然,其實就是把相同類型的變量有序的放在一起。因此,在處理比較多的數(shù)據(jù)時(這也是大多數(shù)的情況)數(shù)組的應(yīng)用范圍是非常廣的。具體的實際應(yīng)用不便舉例,而且絕大多數(shù)是與指針相結(jié)合的,筆者個人認(rèn)為學(xué)習(xí)數(shù)組在更大程度上是為學(xué)習(xí)指針做一個鋪墊。作為基礎(chǔ)的基礎(chǔ)要明白幾種基本操作:即數(shù)組賦值、打印、排序(冒泡排序法和選擇排序法)、查找。這些都不可避免的用到循環(huán),如果覺得反應(yīng)不過來,可以先一點點的把循環(huán)展開,就會越來越熟悉,以后自己編寫一個功能的時候就會先找出內(nèi)在規(guī)律,較好的運用了。另外數(shù)組做參數(shù)時,一維的[]里可以是空的,二維的第一個[]里可以是空的但是第二個[]中必須規(guī)定大小。冒泡法排序函數(shù):voidbubble(inta[],intn){inti,j,k;for(i=1,i<n;i++)for(j=0;j<n-i-1;j++)if(a[j]>a[j+1]){k=a[j];a[j]=a[j+1];a[j+1]=k;}}選擇法排序函數(shù):voidsort(inta[],intn){inti,j,k,t;for(i=0,i<n-1;i++){k=i;for(j=i+1;j<n;j++)if(a[k]<a[j])k=j;if(k!=i){t=a[i];a[i]=a[k];a[k]=t;}}}折半查找函數(shù)(原數(shù)組有序):voidsearch(inta[],intn,intx){intleft=0,right=n-1,mid,flag=0;while((flag==0)&&(left<=right)){mid=(left+right)/2;if(x==a[mid]){printf("%d%d",x,mid);flag=1;}elseif(x<a[mid])right=mid-1;elseleft=mid+1;}}相關(guān)常用的算法還有判斷回文,求階乘,F(xiàn)ibanacci數(shù)列,任意進制轉(zhuǎn)換,楊輝三角形計算等等。字符串:字符串其實就是一個數(shù)組(指針),在scanf的輸入列中是不需要在前面加“&”符號的,因為字符數(shù)組名本身即代表地址。值得注意的是字符串末尾的‘’,如果沒有的話,字符串很有可能會不正常的打印。另外就是字符串的定義和賦值問題了,筆者有一次的比較綜合的上機作業(yè)就是字符串打印老是亂碼,上上下下找了一圈問題,最后發(fā)現(xiàn)是因為char*name;而不是charname[10];前者沒有說明指向哪兒,更沒有確定大小,導(dǎo)致了亂碼的錯誤,印象挺深刻的。另外,字符串的賦值也是需要注意的,如果是用字符指針的話,既可以定義的時候賦初值,即char*a="Abcdefg";也可以在賦值語句中賦值,即char*a;a="Abcdefg";但如果是用字符數(shù)組的話,就只能在定義時整體賦初值,即chara[5]={"abcd"};而不能在賦值語句中整體賦值。常用字符串函數(shù)列表如下,要會自己實現(xiàn):函數(shù)作用 函數(shù)調(diào)用形式 備注字符串拷貝函數(shù) strcpy(char*,char*) 后者拷貝到前者字符串追加函數(shù) strcat(char*,char*) 后者追加到前者后,返回前者,因此前者空間要足夠大字符串比較函數(shù) strcmp(char*,char*) 前者等于、小于、大于后者時,返回0、正值、負(fù)值。注意,不是比較長度,是比較字符ASCII碼的大小,可用于按姓名字母排序等。字符串長度 strlen(char*) 返回字符串的長度,不包括''.轉(zhuǎn)義字符算一個字符。字符串型->整型 atoi(char*)整型->字符串型 itoa(int,char*,int) 做課設(shè)時挺有用的 sprintf(char*,格式化輸入) 賦給字符串,而不打印出來。課設(shè)時用也比較方便注:對字符串是不允許做==或!=的運算的,只能用字符串比較函數(shù)指針:指針可以說是C語言中最關(guān)鍵的地方了,其實這個“指針”的名字對于這個概念的理解是十分形象的。首先要知道,指針變量的值(即指針變量中存放的值)是指針(即地址)。指針變量定義形式中:基本類型*指針變量名中的“*”代表的是這是一個指向該基本類型的指針變量,而不是內(nèi)容的意思。在以后使用的時候,如*ptr=a時,“*”才表示ptr所指向的地址里放的內(nèi)容是a。指針比較典型又簡單的一應(yīng)用例子是兩數(shù)互換,看下面的程序,swap(intc,intd){intt;t=c;c=d;d=t;}main(){inta=2,b=3;swap(a,b);printf(“%d,%d”,a,b);}這是不能實現(xiàn)a和b的數(shù)值互換的,實際上只是形參在這個函數(shù)中換來換去,對實參沒什么影響。現(xiàn)在,用指針類型的數(shù)據(jù)做為參數(shù)的話,更改如下:swap(#3333FF*p1,int*p2){intt;t=*p1;*p1=*p2;*p2=t;}main(){inta=2,b=3;int*ptr1,*ptr2;ptr1=&a;ptr2=&b;swap(prt1,ptr2);printf(“%d,%d”,a,b);}這樣在swap中就把p1,p2的內(nèi)容給換了,即把a,b的值互換了。指針可以執(zhí)行增、減運算,結(jié)合++運算符的法則,我們可以看到:*++s 取指針變量加1以后的內(nèi)容*s++ 取指針變量所指內(nèi)容后s再加1(*s)++ 指針變量指的內(nèi)容加1指針和數(shù)組實際上幾乎是一樣的,數(shù)組名可以看成是一個常量指針,一維數(shù)組中ptr=&b[0]則下面的表示法是等價的:a[3]等價于*(a+3)ptr[3]等價于*(ptr+3)下面看一個用指針來自己實現(xiàn)atoi(字符串型->整型)函數(shù):intatoi(char*s){intsign=1,m=0;if(*s=='+'||*s=='-')/*判斷是否有符號*/sign=(*s++=='+')?1:-1;/*用到三目運算符*/while(*s!='')/*對每一個字符進行操作*/{m=m*10+(*s-'0');s++;/*指向下一個字符*/}returnm*sign;}指向多維數(shù)組的指針變量也是一個比較廣泛的運用。例如數(shù)組a[3][4],a代表的實際是整個二維數(shù)組的首地址,即第0行的首地址,也就是一個指針變量。而a+1就不是簡單的在數(shù)值上加上1了,它代表的不是a[0][1],而是第1行的首地址,&a[1][0]。指針變量常用的用途還有把指針作為參數(shù)傳遞給其他函數(shù),即指向函數(shù)的指針??聪旅娴膸仔写a:voidInput(ST*);voidOutput(ST*);voidBubble(ST*);voidFind(ST*);voidFailure(ST*);/*函數(shù)聲明:這五個函數(shù)都是以一個指向ST型(事先定義過)結(jié)構(gòu)的指針變量作為參數(shù),無返回值。*/void(*process[5])(ST*)={Input,Output,Bubble,Find,Failure};/*process被調(diào)用時提供5種功能不同的函數(shù)共選擇(指向函數(shù)的指針數(shù)組)*/printf("\nChoose:\n?");scanf("%d",&choice);if(choice>=0&&choice<=4)(*process[choice])(a);/*調(diào)用相應(yīng)的函數(shù)實現(xiàn)不同功能*;/總之,指針的應(yīng)用是非常靈活和廣泛的,不是三言兩語能說完的,上面幾個小例子只是個引子,實際編程中,會逐漸發(fā)現(xiàn)運用指針?biāo)軒淼谋憷透咝省?. C語言難點這篇文章主要是介紹一些在復(fù)習(xí)C語言的過程中筆者個人認(rèn)為比較重點的地方,較好的掌握這些重點會使對C的運用更加得心應(yīng)手。此外會包括一些細(xì)節(jié)、易錯的地方。涉及的主要內(nèi)容包括:變量的作用域和存儲類別、函數(shù)、數(shù)組、字符串、指針、文件、鏈表等。一些最基本的概念在此就不多作解釋了,僅希望能有只言片語給同是C語言初學(xué)者的學(xué)習(xí)和上機過程提供一點點的幫助。變量作用域和存儲類別:了解了基本的變量類型后,我們要進一步了解它的存儲類別和變量作用域問題。變量類別 子類別局部變量 靜態(tài)變量(離開函數(shù),變量值仍保留) 自動變量 寄存器變量全局變量 靜態(tài)變量(只能在本文件中用) 非靜態(tài)變量(允許其他文件使用)換一個角度變量類別 子類別靜態(tài)存儲變量 靜態(tài)局部變量(函數(shù)) 靜態(tài)全局變量(本文件) 非靜態(tài)全局/外部變量(其他文件引用)動態(tài)存儲變量 自動變量 寄存器變量 形式參數(shù)extern型的存儲變量在處理多文件問題時常能用到,在一個文件中定義extern型的變量即說明這個變量用的是其他文件的。順便說一下,筆者在做課設(shè)時遇到outofmemory的錯誤,于是改成做多文件,再把它include進來(注意自己寫的*.h要用“”不用<>),能起到一定的效用。static型的在讀程序?qū)懡Y(jié)果的試題中是個考點。多數(shù)時候整個程序會出現(xiàn)多個定義的變量在不同的函數(shù)中,考查在不同位置同一變量的值是多少。主要是遵循一個原則,只要本函數(shù)內(nèi)沒有定義的變量就用全局變量(而不是main里的),全局變量和局部變量重名時局部變量起作用,當(dāng)然還要注意靜態(tài)與自動變量的區(qū)別。函數(shù):對于函數(shù)最基本的理解是從那個叫main的單詞開始的,一開始總會覺得把語句一并寫在main里不是挺好的么,為什么偏擇出去。其實這是因為對函數(shù)還不夠熟練,否則函數(shù)的運用會給我們編程帶來極大的便利。我們要知道函數(shù)的返回值類型,參數(shù)的類型,以及調(diào)用函數(shù)時的形式。事先的函數(shù)說明也能起到一個提醒的好作用。所謂形參和實參,即在調(diào)用函數(shù)時寫在括號里的就是實參,函數(shù)本身用的就是形參,在畫流程圖時用平行四邊形表示傳參。函數(shù)的另一個應(yīng)用例子就是遞歸了,筆者開始比較頭疼的問題,反應(yīng)總是比較遲鈍,按照老師的方法,把遞歸的過程耐心準(zhǔn)確的逐級畫出來,學(xué)習(xí)的效果還是比較好的,會覺得這種遞歸的運用是挺巧的,事實上,著名的八皇后、漢諾塔等問題都用到了遞歸。例子:longfun(intn){longs;if(n==1||n==2)s=2;elses=n-fun(n-1);returns;}main(){printf("%ld",fun(4));}數(shù)組:分為一維數(shù)組和多維數(shù)組,其存儲方式畫為表格的話就會一目了然,其實就是把相同類型的變量有序的放在一起。因此,在處理比較多的數(shù)據(jù)時(這也是大多數(shù)的情況)數(shù)組的應(yīng)用范圍是非常廣的。具體的實際應(yīng)用不便舉例,而且絕大多數(shù)是與指針相結(jié)合的,筆者個人認(rèn)為學(xué)習(xí)數(shù)組在更大程度上是為學(xué)習(xí)指針做一個鋪墊。作為基礎(chǔ)的基礎(chǔ)要明白幾種基本操作:即數(shù)組賦值、打印、排序(冒泡排序法和選擇排序法)、查找。這些都不可避免的用到循環(huán),如果覺得反應(yīng)不過來,可以先一點點的把循環(huán)展開,就會越來越熟悉,以后自己編寫一個功能的時候就會先找出內(nèi)在規(guī)律,較好的運用了。另外數(shù)組做參數(shù)時,一維的[]里可以是空的,二維的第一個[]里可以是空的但是第二個[]中必須規(guī)定大小。冒泡法排序函數(shù):voidbubble(inta[],intn){inti,j,k;for(i=1,i<n;i++)for(j=0;j<n-i;j++)if(a[j]>a[j+1]){k=a[j];a[j]=a[j+1];a[j+1]=k;}}選擇法排序函數(shù):voidsort(inta[],intn){inti,j,k,t;for(i=0,i<n-1;i++){k=i;for(j=i+1;j<n;j++)if(a[k]<a[j])k=j;if(k!=i){t=a[i];a[i]=a[k];a[k]=t;}}}折半查找函數(shù)(原數(shù)組有序):voidsearch(inta[],intn,intx){intleft=0,right=n-1,mid,flag=0;while((flag==0)&&(left<=right)){mid=(left+right)/2;if(x==a[mid]){printf("%d%d",x,mid);flag=1;}elseif(x<a[mid])right=mid-1;elseleft=mid+1;}}相關(guān)常用的算法還有判斷回文,求階乘,F(xiàn)ibanacci數(shù)列,任意進制轉(zhuǎn)換,楊輝三角形計算等等。字符串:字符串其實就是一個數(shù)組(指針),在scanf的輸入列中是不需要在前面加“&”符號的,因為字符數(shù)組名本身即代表地址。值得注意的是字符串末尾的‘\0’,如果沒有的話,字符串很有可能會不正常的打印。另外就是字符串的定義和賦值問題了,筆者有一次的比較綜合的上機作業(yè)就是字符串打印老是亂碼,上上下下找了一圈問題,最后發(fā)現(xiàn)是因為char*name;而不是charname[10];前者沒有說明指向哪兒,更沒有確定大小,導(dǎo)致了亂碼的錯誤,印象挺深刻的。另外,字符串的賦值也是需要注意的,如果是用字符指針的話,既可以定義的時候賦初值,即char*a="Abcdefg";也可以在賦值語句中賦值,即char*a;a="Abcdefg";但如果是用字符數(shù)組的話,就只能在定義時整體賦初值,即chara[5]={"abcd"};而不能在賦值語句中整體賦值。常用字符串函數(shù)列表如下,要會自己實現(xiàn):函數(shù)作用 函數(shù)調(diào)用形式 備注字符串拷貝函數(shù) strcpy(char*,char*) 后者拷貝到前者字符串追加函數(shù) strcat(char*,char*) 后者追加到前者后,返回前者,因此前者空間要足夠大字符串比較函數(shù) strcmp(char*,char*) 前者等于、小于、大于后者時,返回0、正值、負(fù)值。注意,不是比較長度,是比較字符ASCII碼的大小,可用于按姓名字母排序等。字符串長度 strlen(char*) 返回字符串的長度,不包括'\0'.轉(zhuǎn)義字符算一個字符。字符串型->整型 atoi(char*)整型->字符串型 itoa(int,char*,int) 做課設(shè)時挺有用的 sprintf(char*,格式化輸入) 賦給字符串,而不打印出來。課設(shè)時用也比較方便注:對字符串是不允許做==或!=的運算的,只能用字符串比較函數(shù)指針:指針可以說是C語言中最關(guān)鍵的地方了,其實這個“指針”的名字對于這個概念的理解是十分形象的。首先要知道,指針變量的值(即指針變量中存放的值)是指針(即地址)。指針變量定義形式中:基本類型*指針變量名中的“*”代表的是這是一個指向該基本類型的指針變量,而不是內(nèi)容的意思。在以后使用的時候,如*ptr=a時,“*”才表示ptr所指向的地址里放的內(nèi)容是a。指針比較典型又簡單的一應(yīng)用例子是兩數(shù)互換,看下面的程序,swap(intc,intd){intt;t=c;c=d;d=t;}main(){inta=2,b=3;swap(a,b);printf(“%d,%d”,a,b);}這是不能實現(xiàn)a和b的數(shù)值互換的,實際上只是形參在這個函數(shù)中換來換去,對實參沒什么影響?,F(xiàn)在,用指針類型的數(shù)據(jù)做為參數(shù)的話,更改如下:swap(#3333FF*p1,int*p2){intt;t=*p1;*p1=*p2;*p2=t;}main(){inta=2,b=3;int*ptr1,*ptr2;ptr1=&a;ptr2=&b;swap(prt1,ptr2);printf(“%d,%d”,a,b);}這樣在swap中就把p1,p2的內(nèi)容給換了,即把a,b的值互換了。指針可以執(zhí)行增、減運算,結(jié)合++運算符的法則,我們可以看到:*++s 取指針變量加1以后的內(nèi)容*s++ 取指針變量所指內(nèi)容后s再加1(*s)++ 指針變量指的內(nèi)容加1指針和數(shù)組實際上幾乎是一樣的,數(shù)組名可以看成是一個常量指針,一維數(shù)組中ptr=&b[0]則下面的表示法是等價的:a[3]等價于*(a+3)ptr[3]等價于*(ptr+3)下面看一個用指針來自己實現(xiàn)atoi(字符串型->整型)函數(shù):intatoi(char*s){intsign=1,m=0;if(*s=='+'||*s=='-')/*判斷是否有符號*/sign=(*s++=='+')?1:-1;/*用到三目運算符*/while(*s!='\0')/*對每一個字符進行操作*/{m=m*10+(*s-'0');s++;/*指向下一個字符*/}returnm*sign;}指向多維數(shù)組的指針變量也是一個比較廣泛的運用。例如數(shù)組a[3][4],a代表的實際是整個二維數(shù)組的首地址,即第0行的首地址,也就是一個指針變量。而a+1就不是簡單的在數(shù)值上加上1了,它代表的不是a[0][1],而是第1行的首地址,&a[1][0]。指針變量常用的用途還有把指針作為參數(shù)傳遞給其他函數(shù),即指向函數(shù)的指針??聪旅娴膸仔写a:voidInput(ST*);voidOutput(ST*);voidBubble(ST*);voidFind(ST*);voidFailure(ST*);/*函數(shù)聲明:這五個函數(shù)都是以一個指向ST型(事先定義過)結(jié)構(gòu)的指針變量作為參數(shù),無返回值。*/void(*process[5])(ST*)={Input,Output,Bubble,Find,Failure};/*process被調(diào)用時提供5種功能不同的函數(shù)共選擇(指向函數(shù)的指針數(shù)組)*/printf("\nChoose:\n?");scanf("%d",&choice);if(choice>=0&&choice<=4)(*process[choice])(a);/*調(diào)用相應(yīng)的函數(shù)實現(xiàn)不同功能*;/總之,指針的應(yīng)用是非常靈活和廣泛的,不是三言兩語能說完的,上面幾個小例子只是個引子,實際編程中,會逐漸發(fā)現(xiàn)運用指針?biāo)軒淼谋憷透咝?。文件:函?shù)調(diào)用形式 說明fopen("路徑","打開方式") 打開文件fclose(FILE*) 防止之后被誤用fgetc(FILE*) 從文件中讀取一個字符fputc(ch,FILE*) 把ch代表的字符寫入這個文件里fgets(FILE*) 從文件中讀取一行fputs(FILE*) 把一行寫入文件中fprintf(FILE*,"格式字符串",輸出表列) 把數(shù)據(jù)寫入文件fscanf(FILE*,"格式字符串",輸入表列) 從文件中讀取fwrite(地址,sizeof(),n,F(xiàn)ILE*) 把地址中n個sizeof大的數(shù)據(jù)寫入文件里fread(地址,sizeof(),n,F(xiàn)ILE*) 把文件中n個sizeof大的數(shù)據(jù)讀到地址里rewind(FILE*) 把文件指針撥回到文件頭fseek(FILE*,x,0/1/2) 移動文件指針。第二個參數(shù)是位移量,0代表從頭移,1代表從當(dāng)前位置移,2代表從文件尾移。feof(FILE*) 判斷是否到了文件末尾文件打開方式 說明r 打開只能讀的文件w 建立供寫入的文件,如果已存在就抹去原有數(shù)據(jù)a 打開或建立一個把數(shù)據(jù)追加到文件尾的文件r+ 打開用于更新數(shù)據(jù)的文件w+ 建立用于更新數(shù)據(jù)的文件,如果已存在就抹去原有數(shù)據(jù)a+ 打開或建立用于更新數(shù)據(jù)的文件,數(shù)據(jù)追加到文件尾注:以上用于文本文件的操作,如果是二進制文件就在上述字母后加“b”。我們用文件最大的目的就是能讓數(shù)據(jù)保存下來。因此在要用文件中數(shù)據(jù)的時候,就是要把數(shù)據(jù)讀到一個結(jié)構(gòu)(一般保存數(shù)據(jù)多用結(jié)構(gòu),便于管理)中去,再對結(jié)構(gòu)進行操作即可。例如,文件aa.data中存儲的是30個學(xué)生的成績等信息,要遍歷這些信息,對其進行成績輸出、排序、查找等工作時,我們就把這些信息先讀入到一個結(jié)構(gòu)數(shù)組中,再對這個數(shù)組進行操作。如下例:#include<stdio.h>#include<stdlib.h>#defineN30typedefstructstudent/*定義儲存學(xué)生成績信息的數(shù)組*/{char*name;intchinese;intmaths;intphy;inttotal;}ST;main(){STa[N];/*存儲N個學(xué)生信息的數(shù)組*/FILE*fp;void(*process[3])(ST*)={Output,Bubble,Find};/*實現(xiàn)相關(guān)功能的三個函數(shù)*/intchoice,i=0;Show();printf("\nChoose:\n?");scanf("%d",&choice);while(choice>=0&&choice<=2){fp=fopen("aa.dat","rb");for(i=0;i<N;i++)fread(&a[i],sizeof(ST),1,fp);/*把文件中儲存的信息逐個讀到數(shù)組中去*/fclose(fp);(*process[choice])(a);/*前面提到的指向函數(shù)的指針,選擇操作*/printf("\n");Show();printf("\n?");scanf("%d",&choice);}}voidShow(){printf("\n****Choices:****\n0.Displaythedataform\n1.Bubbleitaccordingtothetotalscore\n2.Search\n3.Quit!\n");}voidOutput(ST*a)/*將文件中存儲的信息逐個輸出*/{inti,t=0;printf("NameChineseMathsPhysicsTotal\n");for(i=0;i<N;i++){t=a[i].chinese+a[i].maths+a[i].phy;a[i].total=t;printf("%4s%8d%8d%8d%8d\n",a[i].name,a[i].chinese,a[i].maths,a[i].phy,a[i].total);}}voidBubble(ST*a)/*對數(shù)組進行排序,并輸出結(jié)果*/{inti,pass;STm;for(pass=0;pass<N-1;pass++)for(i=0;i<N-1;i++)if(a[i].total<a[i+1].total){m=a[i];/*結(jié)構(gòu)互換*/a[i]=a[i+1];a[i+1]=m;}Output(a);}voidFind(ST*a){inti,t=1;charm[20];printf("\nEnterthenameyouwant:");scanf("%s",m);for(i=0;i<N;i++)if(!strcmp(m,a[i].name))/*根據(jù)姓名匹配情況輸出查找結(jié)果*/{printf("\nTheresultis:\n%s,Chinese:%d,Maths:%d,Physics:%d,Total:%d\n",m,a[i].chinese,a[i].maths,a[i].phy,a[i].total);t=0;}if(t)printf("\nThenameisnotinthelist!\n");}鏈表:鏈表是C語言中另外一個難點。牽扯到結(jié)點、動態(tài)分配空間等等。用結(jié)構(gòu)作為鏈表的結(jié)點是非常適合的,例如:structnode{intdata;structnode*next;};其中next是指向自身所在結(jié)構(gòu)類型的指針,這樣就可以把一個個結(jié)點相連,構(gòu)成鏈表。鏈表結(jié)構(gòu)的一大優(yōu)勢就是動態(tài)分配存儲,不會像數(shù)組一樣必須在定義時確定大小,造成不必要的浪費。用malloc和free函數(shù)即可實現(xiàn)開辟和釋放存儲單元。其中,malloc的參數(shù)多用sizeof運算符計算得到。鏈表的基本操作有:正、反向建立鏈表;輸出鏈表;刪除鏈表中結(jié)點;在鏈表中插入結(jié)點等等,都是要熟練掌握的,初學(xué)者通過畫圖的方式能比較形象地理解建立、插入等實現(xiàn)的過程。typedefstructnode{chardata;structnode*next;}NODE;/*結(jié)點*/正向建立鏈表:NODE*create(){charch='a';NODE*p,*h=NULL,*q=NULL;while(ch<'z'){p=(NODE*)malloc(sizeof(NODE));/*強制類型轉(zhuǎn)換為指針*/p->data=ch;if(h==NULL)h=p;elseq->next=p;ch++;q=p;}q->next=NULL;/*鏈表結(jié)束*/returnh;}逆向建立:NODE*create(){charch='a';NODE*p,*h=NULL;while(ch<='z'){p=(NODE*)malloc(sizeof(NODE));p->data=ch;p->next=h;/*不斷地把head往前挪*/h=p;ch++;}returnh;}用遞歸實現(xiàn)鏈表逆序輸出:voidoutput(NODE*h){if(h!=NULL){output(h->next);printf("%c",h->data);}}插入結(jié)點(已有升序的鏈表):NODE*insert(NODE*h,intx){NODE*new,*front,*current=h;while(current!=NULL&&(current->data<x))/*查找插入的位置*/{front=current;current=current->next;}new=(NODE*)malloc(sizeof(NODE));new->data=x;new->next=current;if(current==h)/*判斷是否是要插在表頭*/h=new;elsefront->next=new;returnh;}刪除結(jié)點:NODE*delete(NODE*h,intx){NODE*q,*p=h;while(p!=NULL&&(p->data!=x)){q=p;p=p->next;}if(p->data==x)/*找到了要刪的結(jié)點*/{if(p==h)/*判斷是否要刪表頭*/h=h->next;elseq->next=p->next;free(p);/*釋放掉已刪掉的結(jié)點*/}returnh;}4. C/C++實現(xiàn)冒泡排序算法最簡單的排序方法是冒泡排序方法。這種方法的基本思想是,將待排序的元素看作是豎著排列的“氣泡”,較小的元素比較輕,從而要往上浮。在冒泡排序算法中我們要對這個“氣泡”序列處理若干遍。所謂一遍處理,就是自底向上檢查一遍這個序列,并時刻注意兩個相鄰的元素的順序是否正確。如果發(fā)現(xiàn)兩個相鄰元素的順序不對,即“輕”的元素在下面,就交換它們的位置。顯然,處理一遍之后,“最輕”的元素就浮到了最高位置;處理二遍之后,“次輕”的元素就浮到了次高位置。在作第二遍處理時,由于最高位置上的元素已是“最輕”元素,所以不必檢查。一般地,第i遍處理時,不必檢查第i高位置以上的元素,因為經(jīng)過前面i-1遍的處理,它們已正確地排好序。這個算法可實現(xiàn)如下。BubbleSort程序:STLC++程序:(VC++6.0通過)#include"stdafx.h"#include"iostream.h"template<classT>classdoit{private:intx,y;Ttemp;public:doit(T*in,intcount){for(y=0;y<count-1;y++){for(x=1;x<count-y;x++){if((*(in+x))>(*(in+x-1))){temp=(*(in+x-1));(*(in+x-1))=(*(in+x));(*(in+x))=temp;}}}}};intmain(){doublea[4]={1.1,1.3,1.9,2.2};doit<double>d(a,4);for(inti=0;i<4;i++){cout<<a[i]<<endl;}return0;}C語言程序:(TC2.0通過)voiddoit(float*in,intcount){intx;inty;floattemp;for(y=0;y<count-1;y++){for(x=1;x<count-y;x++){if((*(in+x))>(*(in+x-1))){temp=(*(in+x-1));(*(in+x-1))=(*(in+x));(*(in+x))=temp;}}}}5. C++中指針和引用的區(qū)別引用和指針★相同點:1.都是地址的概念;指針指向一塊內(nèi)存,它的內(nèi)容是所指內(nèi)存的地址;引用是某塊內(nèi)存的別名?!飬^(qū)別:1.指針是一個實體,而引用僅是個別名;2.引用使用時無需解引用(*),指針需要解引用;3.引用只能在定義時被初始化一次,之后不可變;指針可變;引用“從一而終”^_^4.引用沒有const,指針有const,const的指針不可變;5.引用不能為空,指針可以為空;6.“sizeof引用”得到的是所指向的變量(對象)的大小,而“sizeof指針”得到的是指針本身(所指向的變量或?qū)ο蟮牡刂?的大??;typeid(T)==typeid(T&)恒為真,sizeof(T)==sizeof(T&)恒為真,但是當(dāng)引用作為成員時,其占用空間與指針相同(沒找到標(biāo)準(zhǔn)的規(guī)定)。7.指針和引用的自增(++)運算意義不一樣;★聯(lián)系1.引用在語言內(nèi)部用指針實現(xiàn)(如何實現(xiàn)?)。2.對一般應(yīng)用而言,把引用理解為指針,不會犯嚴(yán)重語義錯誤。引用是操作受限了的指針(僅容許取內(nèi)容操作)。6. constchar*,charconst*,char*const的區(qū)別constchar*,charconst*,char*const的區(qū)別問題幾乎是C++面試中每次都會有的題目。事實上這個概念誰都有只是三種聲明方式非常相似很容易記混。Bjarne在他的TheC++ProgrammingLanguage里面給出過一個助記的方法:把一個聲明從右向左讀。char*constcp;(*讀成pointertocpisaconstpointertocharconstchar*p;pisapointertoconstchar;charconst*p;同上因為C++里面沒有const*的運算符,所以const只能屬于前面的類型。C++標(biāo)準(zhǔn)規(guī)定,const關(guān)鍵字放在類型或變量名之前等價的。constintn=5;//sameasbelowintconstm=10;constint*p;//sameasbelowconst(int)*pintconst*q;//(int)const*pchar**p1;//pointertopointertocharconstchar**p2;//pointertopointertoconstcharchar*const*p3;//pointertoconstpointertocharconstchar*const*p4;//pointertoconstpointertoconstcharchar**constp5;//constpointertopointertocharconstchar**constp6;//constpointertopointertoconstcharchar*const*constp7;//constpointertoconstpointertocharconstchar*const*constp8;//constpointertoconstpointertoconstchar7. C中可變參數(shù)函數(shù)實現(xiàn)一、從printf()開始原型:intprintf(constchar*format,...);參數(shù)format表示如何來格式字符串的指令,…表示可選參數(shù),調(diào)用時傳遞給"..."的參數(shù)可有可無,根據(jù)實際情況而定。系統(tǒng)提供了vprintf系列格式化字符串的函數(shù),用于編程人員封裝自己的I/O函數(shù)。intvprintf/vscanf(constchar*format,va_listap);//從標(biāo)準(zhǔn)輸入/輸出格式化字符串intvfprintf/vfsacanf(FILE*stream,constchar*format,va_listap);//從文件流intvsprintf/vsscanf(char*s,constchar*format,va_listap);//從字符串//例1:格式化到一個文件流,可用于日志文件FILE*logfile;intWriteLog(constchar*format,...){va_listarg_ptr;va_start(arg_ptr,format);intnWrittenBytes=vfprintf(logfile,format,arg_ptr);va_end(arg_ptr);returnnWrittenBytes;}二、va函數(shù)的定義和va宏C語言支持va函數(shù),作為C語言的擴展--C++同樣支持va函數(shù),但在C++中并不推薦使用,C++引入的多態(tài)性同樣可以實現(xiàn)參數(shù)個數(shù)可變的函數(shù)。不過,C++的重載功能畢竟只能是有限多個可以預(yù)見的參數(shù)個數(shù)。比較而言,C中的va函數(shù)則可以定義無窮多個相當(dāng)于C++的重載函數(shù),這方面C++是無能為力的。va函數(shù)的優(yōu)勢表現(xiàn)在使用的方便性和易用性上,可以使代碼更簡潔。C編譯器為了統(tǒng)一在不同的硬件架構(gòu)、硬件平臺上的實現(xiàn),和增加代碼的可移植性,提供了一系列宏來屏蔽硬件環(huán)境不同帶來的差異。ANSIC標(biāo)準(zhǔn)下,va的宏定義在stdarg.h中,它們有:va_list,va_start(),va_arg(),va_end()。三、編譯器如何實現(xiàn)va簡單地說,va函數(shù)的實現(xiàn)就是對參數(shù)指針的使用和控制。typedefchar*va_list;//x86平臺下va_list的定義函數(shù)的固定參數(shù)部分,可以直接從函數(shù)定義時的參數(shù)名獲得;對于可選參數(shù)部分,先將指針指向第一個可選參數(shù),然后依次后移指針,根據(jù)與結(jié)束標(biāo)志的比較來判斷是否已經(jīng)獲得全部參數(shù)。因此,va函數(shù)中結(jié)束標(biāo)志必須事先約定好,否則,指針會指向無效的內(nèi)存地址,導(dǎo)致出錯。這里,移動指針使其指向下一個參數(shù),那么移動指針時的偏移量是多少呢,沒有具體答案,因為這里涉及到內(nèi)存對齊(alignment)問題,內(nèi)存對齊跟具體使用的硬件平臺有密切關(guān)系,比如大家熟知的32位x86平臺規(guī)定所有的變量地址必須是4的倍數(shù)(sizeof(int)=4)。va機制中用宏_INTSIZEOF(n)來解決這個問題,沒有這些宏,va的可移植性無從談起。首先介紹宏_INTSIZEOF(n),它求出變量占用內(nèi)存空間的大小,是va的實現(xiàn)的基礎(chǔ)。#define_INTSIZEOF(n)((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))#defineva_start(ap,v)(ap=(va_list)&v+_INTSIZEOF(v))//第一個可選參數(shù)地址#defineva_arg(ap,t)(*(t*)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t)))//下一個參數(shù)地址#defineva_end(ap)(ap=va_list0)//將指針置為無效1.va_arg身兼二職:返回當(dāng)前參數(shù),并使參數(shù)指針指向下一個參數(shù)。初看va_arg宏定義很別扭,如果把它拆成兩個語句,可以很清楚地看出它完成的兩個職責(zé)。#defineva_arg(ap,t)(*(t*)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t)))//下一個參數(shù)地址//將(*(t*)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t)))拆成:/*指針ap指向下一個參數(shù)的地址*/1).a(chǎn)p+=_INTSIZEOF(t);//當(dāng)前,ap已經(jīng)指向下一個參數(shù)了/*ap減去當(dāng)前參數(shù)的大小得到當(dāng)前參數(shù)的地址,再強制類型轉(zhuǎn)換后返回它的值*/2).return*(t*)(ap-_INTSIZEOF(t))回想到printf/scanf系列函數(shù)的%d%s之類的格式化指令,我們不難理解這些它們的用途了-明示參數(shù)強制轉(zhuǎn)換的類型。(注:printf/scanf沒有使用va_xxx來實現(xiàn),但原理是一致的。)2.va_end很簡單,僅僅是把指針作廢而已。#defineva_end(ap)(ap=(va_list)0)//x86平臺8. C程序內(nèi)存中組成部分由于歷史原因,C程序一直由下列幾部分組成:?正文段這是由CPU執(zhí)行的機器指令部分。通常,正文段是可共享的,所以即使是經(jīng)常執(zhí)行的程序(如文本編輯程序、C編譯程序、shell等)在存儲器中也只需有一個副本,另外,正文段常常是只讀的,以防止程序由于意外事故而修改其自身的指令。?初始化數(shù)據(jù)段通常將此段稱為數(shù)據(jù)段,它包含了程序中需賦初值的變量。例如,C程序中任何函數(shù)之外的說明:intmaxcount=99;使此變量以初值存放在初始化數(shù)據(jù)段中。?非初始化數(shù)據(jù)段通常將此段稱為bss段,這一名稱來源于早期匯編程序的一個操作符,意思是“blockstartedbysymbol(由符號開始的塊)”,在程序開始執(zhí)行之前,內(nèi)核將此段初始化為0。函數(shù)外的說明:longsum[1000];使此變量存放在非初始化數(shù)據(jù)段中。?棧自動變量以及每次函數(shù)調(diào)用時所需保存的信息都存放在此段中。每次函數(shù)調(diào)用時,其返回地址、以及調(diào)用者的環(huán)境信息(例如某些機器寄存器)都存放在棧中。然后,新被調(diào)用的函數(shù)在棧上為其自動和臨時變量分配存儲空間。通過以這種方式使用棧,C函數(shù)可以遞歸調(diào)用。?堆通常在堆中進行動態(tài)存儲分配。由于歷史上形成的慣例,堆位于非初始化數(shù)據(jù)段頂和棧底之間。9. C編程拾粹A:多維數(shù)組初始化,如test[2][3][4]=...{...{...{000,001,002,003}...{010,011,012,013}...{020,021,022,023}}...{...{100,101,102,103}...{110,111,112,113}...{120,121,122,123}}}這種初始化風(fēng)格結(jié)構(gòu)清晰,給人的感覺是數(shù)組元素的對應(yīng)位置準(zhǔn)確,如果需要對數(shù)組中某幾個元素賦值時,這種結(jié)構(gòu)就會顯得很合理了。同時最重要的是它的可讀性很強,便于維護。其中的花括號起到一種定界的作用。B:看這兩個表達(dá)式:test[i]*(test+i)指針比引用數(shù)組下標(biāo)的效率可能要高,但是引用數(shù)組下標(biāo)不可能比指針的效率高。在一般的情況下,我們多采用指針代替數(shù)組,但是如果以犧牲程序的可讀性和可維護性為代價去換取高效率這不是一個好想法,同時這種做法也違背了軟件工程的思想。C:一點技巧:CODE:/*findkeywordinsourcefile*/#include<stdio.h>#include<string.h>#definenull0intmain(){intlook_upTarget(charconst*t,charconst*s[]);charconst*t="target";charconst*source[]={"hacker","blog","baby","good","test","target",null/*在這里我們用了一個空指針,那么在look_upTarget函數(shù)中就不需要知道和計算source中元素的個數(shù)了*/};printf("%d",look_upTarget(t,source));getch();}intlook_upTarget(charconst*t,charconst*s[]){for(;*s!=null;s++)if(strcmp(t,*s)==0)return1;return-1;}總結(jié):1,數(shù)組名作為一個指針常量,一般來說指向數(shù)組的第一個元素的地址,但是也有一些特殊情況,如sizeof(source)和sizeof(source[0]),sizeof(source[1])等,他們分別計算數(shù)組source所占用的內(nèi)存空間和每一個數(shù)組元素所占用的空間。2,在一般情況下,我們都將函數(shù)形式參數(shù)的中的指針類型定義為const類型,防止間接尋址對主函數(shù)中的值做了不可預(yù)料的修改。10. C語言中實現(xiàn)數(shù)組的動態(tài)增長在c語言中實現(xiàn)數(shù)組的動態(tài)增長原理:在c語言中數(shù)組下標(biāo)訪問可以看成指針偏移訪問1、對表進行檢查,看看它是否真的已滿2、如果表確實已滿,使用realloc()函數(shù)擴展表的長度,并進行檢查,確保realloc()操作成功進行。3、在表中增加所需要的項目。代碼如下:intcurrent_element=0;inttotal_element=128;char*dynamic=malloc(total_element);char*ptr;voidadd_element(charc){if(current_element==total_element-1){total_element*=2;ptr=(char*)realloc(dynamic,total_element);if(ptr==NULL){printf("can'texpandthetable!\n");return-1;}elsedynamic=ptr;}current_element++;dynamic[current_element]=c;}注:在實踐中,不要把realloc()函數(shù)的返回值直接賦給字符指針,如果realloc()函數(shù)失敗,它會使該指針的值變成NULL,這樣就無法對現(xiàn)有的表進行訪問。11. C語言中的位運算在計算機程序中,數(shù)據(jù)的位是可以操作的最小數(shù)據(jù)單位,理論上可以用“位運算”來完成所有的運算和操作。一般的位操作是用來控制硬件的,或者做數(shù)據(jù)變換使用,但是,靈活的位操作可以有效地提高程序運行的效率。C語言提供了位運算的功能,這使得C語言也能像匯編語言一樣用來編寫系統(tǒng)程序。位運算符C語言提供了六種位運算符:&按位與|按位或^按位異或~取反<<左移>>右移1.按位與運算按位與運算符"&"是雙目運算符。其功能是參與運算的兩數(shù)各對應(yīng)的二進位相與。只有對應(yīng)的兩個二進位均為1時,結(jié)果位才為1,否則為0。參與運算的數(shù)以補碼方式出現(xiàn)。例如:9&5可寫算式如下:00001001(9的二進制補碼)&00000101(5的二進制補碼)00000001(1的二進制補碼)可見9&5=1。按位與運算通常用來對某些位清0或保留某些位。例如把a的高八位清0,保留低八位,可作a&255運算(255的二進制數(shù)為0000000011111111)。main(){inta=9,b=5,c;c=a&b;printf("a=%d\nb=%d\nc=%d\n",a,b,c);}應(yīng)用:a.清零特定位(mask中特定位置0,其它位為1,s=s&mask)b.取某數(shù)中指定位(mask中特定位置1,其它位為0,s=s&mask)2.按位或運算按位或運算符“|”是雙目運算符。其功能是參與運算的兩數(shù)各對應(yīng)的二進位相或。只要對應(yīng)的二個二進位有一個為1時,結(jié)果位就為1。參與運算的兩個數(shù)均以補碼出現(xiàn)。例如:9|5可寫算式如下:00001001|0000010100001101(十進制為13)可見9|5=13main(){inta=9,b=5,c;c=a|b;printf("a=%d\nb=%d\nc=%d\n",a,b,c);}應(yīng)用:常用來將源操作數(shù)某些位置1,其它位不變。(mask中特定位置1,其它位為0s=s|mask)3.按位異或運算按位異或運算符“^”是雙目運算符。其功能是參與運算的兩數(shù)各對應(yīng)的二進位相異或,當(dāng)兩對應(yīng)的二進位相異時,結(jié)果為1。參與運算數(shù)仍以補碼出現(xiàn),例如9^5可寫成算式如下:00001001^0000010100001100(十進制為12)main(){inta=9;a=a^15;printf("a=%d\n",a);}應(yīng)用:a.使特定位的值取反(mask中特定位置1,其它位為0s=s^mask)b.不引入第三變量,交換兩個變量的值(設(shè)a=a1,b=b1)目標(biāo)操作操作后狀態(tài)a=a1^b1a=a^ba=a1^b1,b=b1b=a1^b1^b1b=a^ba=a1^b1,b=a1a=b1^a1^a1a=a^ba=b1,b=a14.求反運算求反運算符~為單目運算符,具有右結(jié)合性。其功能是對參與運算的數(shù)的各二進位按位求反。例如~9的運算為:~(0000000000001001)結(jié)果為:11111111111101105.左移運算左移運算符“<<”是雙目運算符。其功能把“<<”左邊的運算數(shù)的各二進位全部左移若干位,由“<<”右邊的數(shù)指定移動的位數(shù),高位丟棄,低位補0。其值相當(dāng)于乘2。例如:a<<4指把a的各二進位向左移動4位。如a=00000011(十進制3),左移4位后為00110000(十進制48)。main(){unsigneda,b;printf("inputanumber:");scanf("%d",&a);b=a>>5;b=b&15;printf("a=%d\tb=%d\n",a,b);}main(){chara='a',b='b';intp,c,d;p=a;p=(p<<8)|b;d=p&0xff;c=(p&0xff00)>>8;printf("a=%d\nb=%d\nc=%d\nd=%d\n",a,b,c,d);}6.右移運算右移運算符“>>”是雙目運算符。其功能是把“>>”左邊的運算數(shù)的各二進位全部右移若干位,“>>”右邊的數(shù)指定移動的位數(shù)。其值相當(dāng)于除2。例如:設(shè)a=15,a>>2表示把000001111右移為00000011(十進制3)。對于左邊移出的空位,如果是正數(shù)則空位補0,若為負(fù)數(shù),可能補0或補1,這取決于所用的計算機系統(tǒng)。移入0的叫邏輯右移,移入1的叫算術(shù)右移,TurboC采用邏輯右移。main(){unsigneda,b;printf("inputanumber:");scanf("%d",&a);b=a>>5;b=b&15;printf("a=%db=%d",a,b);}再看一例:main(){chara='a',b='b';intp,c,d;p=a;p=(p<<8)|b;d=p&0xff;c=(p&0xff00)>>8;printf("a=%db=%dc=%dd=%d",a,b,c,d);}12. 浮點數(shù)的存儲格式:浮點數(shù)的存儲格式是符號+階碼(定點整數(shù))+尾數(shù)(定點小數(shù))SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM即1位符號位(0為正,1為負(fù)),8位指數(shù)位,23位尾數(shù)位浮點數(shù)存儲前先轉(zhuǎn)化成2的k次方形式,即:f=A1*2^k+A2*2^(k-1)+...+Ak+...+An*2^(-m)(Ai={0,1},A1=1)如5.5=2^2+2^0+2^(-1)其中的k就是指數(shù),加127后組成8位指數(shù)位5.5的指數(shù)位就是2+127=129=10000001A2A3.....An就是尾數(shù)位,不足23位后補0所以5.5=01000000101000000000000000000000=40A00000所以,對浮點數(shù)*2、/2只要對8位符號位+、-即可,但不是左移、右移關(guān)于unsignedint和int的在位運算上的不同,下面有個CU上的例子描述的很清楚:[問題]:這個函數(shù)有什么問題嗎?/***本函數(shù)將兩個16比特位的值連結(jié)成為一個32比特位的值。*參數(shù):sHighBits高16位*sLowBits低16位*返回:32位值**/longCatenateBits16(shortsHighBits,shortsLowBits){longlResult=0;/*32位值的臨時變量*//*將第一個16位值放入32位值的高16位*/lRe
溫馨提示
- 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)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 影視原聲帶的出版研究考核試卷
- 2025-2030年廚電產(chǎn)品試用活動行業(yè)深度調(diào)研及發(fā)展戰(zhàn)略咨詢報告
- 2025-2030年數(shù)字化過程信號校驗儀行業(yè)深度調(diào)研及發(fā)展戰(zhàn)略咨詢報告
- 2025-2030年排毒養(yǎng)顏飲料行業(yè)深度調(diào)研及發(fā)展戰(zhàn)略咨詢報告
- 2025-2030年固態(tài)存儲控制器芯片行業(yè)跨境出海戰(zhàn)略研究報告
- 微生物肥料在提高作物產(chǎn)量的作用考核試卷
- 塑膠跑道邊緣處理與安全考量考核試卷
- 基因工程疫苗的多靶點設(shè)計與優(yōu)化考核試卷
- 丙綸纖維的輕量化結(jié)構(gòu)設(shè)計考核試卷
- 二零二五版女方房產(chǎn)放棄離婚協(xié)議書相關(guān)法律法規(guī)解讀
- 公安法制培訓(xùn)
- 電力工程施工售后保障方案
- 中國心力衰竭診斷和治療指南2024解讀(完整版)
- 《鋼鐵是怎樣練成的》閱讀任務(wù)單及答案
- 新人教版高中數(shù)學(xué)必修第二冊第六章平面向量及其應(yīng)用教案 (一)
- 期末 (試題) -2024-2025學(xué)年教科版(廣州)英語四年級上冊
- 解讀國有企業(yè)管理人員處分條例課件
- 湖南省長沙市一中2024-2025學(xué)年高一生物上學(xué)期期末考試試題含解析
- 碳纖維增強復(fù)合材料在海洋工程中的應(yīng)用情況
- 公司市場分析管理制度
- 焊接材料制造工-國家職業(yè)標(biāo)準(zhǔn)(2024版)
評論
0/150
提交評論