C語言常見錯(cuò)誤分析_第1頁
C語言常見錯(cuò)誤分析_第2頁
C語言常見錯(cuò)誤分析_第3頁
C語言常見錯(cuò)誤分析_第4頁
C語言常見錯(cuò)誤分析_第5頁
已閱讀5頁,還剩110頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

C語言常見錯(cuò)誤及問題分析C語言編程常見錯(cuò)誤分析C語言是目前世界上最通用的編程語言之一,也是目前研發(fā)使用最多的編程語言。同各種各樣的bug作斗爭(zhēng),是每一個(gè)C程序員每天所面臨的課題。本文將從微觀角度出發(fā),對(duì)一些常見出錯(cuò)類型的案例進(jìn)行分析,希望大家今后能避免類似的錯(cuò)誤。數(shù)字和表達(dá)式錯(cuò)誤變量的錯(cuò)誤數(shù)組和指針的錯(cuò)誤邏輯和流程的錯(cuò)誤數(shù)字和表達(dá)式的錯(cuò)誤運(yùn)算符和優(yōu)先級(jí)的錯(cuò)誤字節(jié)序的錯(cuò)誤魔鬼數(shù)字宏定義的錯(cuò)誤sizeof的錯(cuò)誤最常見的運(yùn)算符錯(cuò)誤就是“=”和“==”的誤用intmain(){ intret; ret=GetVars(); if(ret=VOS_OK) { ...... } return0; }錯(cuò)誤后果:1.變量被錯(cuò)誤賦值。2.邏輯判斷不正確。建議和結(jié)論:盡管是初級(jí)bug但是還是常有發(fā)生,建議寫成“VOS_OK==ret”的形式,這樣在編譯的時(shí)候即可發(fā)現(xiàn)這種錯(cuò)誤。“++”和“--”在表達(dá)式中的應(yīng)用#definemypower(a)((a)*(a))intmain(){ inti=1,j=2; j=mypower(++i); printf("\r\n%i=dj=%d",i,j); return0; }錯(cuò)誤后果:結(jié)果與期望的不一致建議與結(jié)論:1.對(duì)于“++”和“--”這種基本的知識(shí)還是應(yīng)該掌握的2.自增和自減變量在本表達(dá)式中不要再引用,否則可能依賴編譯器實(shí)現(xiàn)3.沒有把握的用法千萬不要用,否則可能導(dǎo)致意想不到的錯(cuò)誤優(yōu)先級(jí)問題也是編碼初期容易出現(xiàn)的問題C語言有眾多的運(yùn)算符號(hào),它們之間的優(yōu)先級(jí)關(guān)系非常復(fù)雜,即使是一個(gè)熟練的C程序員,要清楚地記住這些優(yōu)先級(jí)關(guān)系也絕非易事。if(high<<8|low)==>if((high<<8)|low)if(a|b&&a&c)==>if((a|b)&&(a&c))if(a|b==1)==>if((a|b)==1)建議和結(jié)論:不要使用默認(rèn)優(yōu)先級(jí),使用括號(hào)來保證自己的運(yùn)算優(yōu)先級(jí),不要考驗(yàn)字節(jié)的記憶力。字節(jié)序錯(cuò)誤網(wǎng)絡(luò)設(shè)備和網(wǎng)絡(luò)協(xié)議的開發(fā)涉及到許多字節(jié)序問題。網(wǎng)絡(luò)序:所有設(shè)備、系統(tǒng)都一樣,表示字節(jié)在網(wǎng)絡(luò)中的傳輸順序,也就是設(shè)備接收、發(fā)送數(shù)據(jù)的順序。數(shù)據(jù)總是按照從“高字節(jié)”===>“低字節(jié)”的順序發(fā)。主機(jī)序:依賴于CPU,表示的是字節(jié)在內(nèi)存中的存放順序。對(duì)于Intel系列CPU:高字節(jié)存放高位,低字節(jié)存放低位;和網(wǎng)絡(luò)序相反;一般稱為“小尾”或者“小端”(littleendian)對(duì)于PPC系列CPU:高字節(jié)存放低位,低字節(jié)存放高位;和網(wǎng)絡(luò)序相同;一般稱為“大尾”或者“打端”(bigendian)對(duì)于32位int型數(shù)0x12345678,intel和ppc系列CPU的存放格式分別如下:假定有一種協(xié)議報(bào)文,報(bào)文類型為2個(gè)字節(jié),某一特定類型為0xABCD兩個(gè)因?yàn)樽止?jié)序錯(cuò)誤導(dǎo)致的真實(shí)案例案例一:某一種協(xié)議,其中一種報(bào)文類型是3,但是在代碼中填寫報(bào)文類型時(shí)沒有進(jìn)行字節(jié)序轉(zhuǎn)換,導(dǎo)致對(duì)端總是識(shí)別報(bào)文類型為0x0300,于是對(duì)端認(rèn)為是非法報(bào)文,直接作丟棄處理后果:當(dāng)時(shí)使用的是Intel的CPU,這種錯(cuò)誤類型的報(bào)文從來沒有被處理過,當(dāng)時(shí)也沒有做單元測(cè)試,錯(cuò)誤很久以后才被發(fā)現(xiàn)。因?yàn)檫@個(gè)報(bào)文是用來進(jìn)行協(xié)議性能優(yōu)化的,修改過后協(xié)議性能得到極大提升。案例二:某一種協(xié)議,報(bào)文中有一個(gè)字段用DWORD表示報(bào)文長(zhǎng)度,但是在填寫報(bào)文時(shí)遺漏了字節(jié)序轉(zhuǎn)換,因?yàn)槭褂玫氖荌ntel的CPU,導(dǎo)致對(duì)端從收到的報(bào)文中提取出的報(bào)文長(zhǎng)度為類似0x4800000000的巨大數(shù)字。我們字節(jié)的設(shè)備在接收時(shí)也遺漏了字節(jié)序轉(zhuǎn)換,所以從收到的報(bào)文中反而能正確提取出報(bào)文長(zhǎng)度,該設(shè)備也一直沒有與cisco的設(shè)備做互通測(cè)試,使該問題一直沒有暴露出來。后果:該設(shè)備第一次開局時(shí),與cisco設(shè)備互通,結(jié)果周邊所有cisco的設(shè)備異常重啟(cisco當(dāng)年設(shè)備的魯棒性也比較差勁?。W止?jié)序錯(cuò)誤不只在報(bào)文中存在一.聯(lián)合域定義:union{ ULONGulIP; UCHARszIP[4]; }stIPAddr;如果賦值:stIPAddr.szIP[0]=192;stIPAddr.szIP[1]=168;stIPAddr.szIP[2]=0;stIPAddr.szIP[3]=1;則大端和小端上stIPAddr.ulIP的值不同:大端:stIPAddr.ulIP=0xC0A80001;小端:stIPAddr.ulIP=0x0100A8C0;二.指針強(qiáng)轉(zhuǎn):定義如下變量:ULONGulTool=0x12345678;USHORT*pusTool=(USHORT*)&ulTool;UCHAR*pucTool=(UCHAR*)&ulTool;則在大端和小端上,*pusTool和*pucTool的值是不同的。大端系統(tǒng):*pusTool=0x1234*pucTool=0x12小端系統(tǒng):*pusTool=0x5678*pucTool=0x78建議和結(jié)論:1.填寫報(bào)文和解析報(bào)文時(shí)一定要注意字節(jié)序問題,使用相應(yīng)的宏操作(htonl、ntohl等)2.當(dāng)取變量的一部分值時(shí),如聯(lián)合體、強(qiáng)制指針轉(zhuǎn)換等,需要考慮字節(jié)序問題3.互通測(cè)試很重要魔鬼數(shù)字問題魔鬼數(shù)字是指直接使用數(shù)字,而不是使用預(yù)先定義好的宏、常量、枚舉等,這是一種不好的編程習(xí)慣。如下:l=round/3.14159;DeadInt=HelloInt*4;pstPack=malloc(36);pTcp=pIp+20;constdoublePI=3.14159;l=round/PI;#defineDEADTIME4DeadInt=HelloInt*DEADTIME;#definePACKSIZE36pstPack=malloc(PACKSIZE);#defineIPHEADLEN20pTcp=pIp+IPHEADLEN;建議和結(jié)論:1.魔鬼數(shù)字是一種不好的編程習(xí)慣,一方面代碼可讀性差,另一方面在修改多出數(shù)字時(shí)容易造成遺漏,從而各處使用導(dǎo)致不一致。2.不是所有的數(shù)字都是魔鬼數(shù)字3.有明確意義的數(shù)字應(yīng)該定義長(zhǎng)宏、枚舉或者常量,如申請(qǐng)的內(nèi)存大小、函數(shù)的返回值、各種標(biāo)記位等。宏定義錯(cuò)誤宏定義最常見的問題就是沒有使用足夠的括號(hào)去保證展開的正確性。示例1:#definemul1(a,b)(a*b)#definemul2(a,b)((a)*(b))intmain(){ intx=0; x=mul1(1+2,5);/*x=11*/ x=mul2(1+2,5);/*x=15*/}示例2:#defineadd1(a,b)(a)+(b)#defineadd2(a,b)((a)+(b))intmain(){ intx=0; x=add1(1+2)*5;/*x=11*/ x=add2(1+2)*5;/*x=15*/}建議和結(jié)論:1.宏定義會(huì)忠實(shí)地進(jìn)行展開,這個(gè)展開過程忽略運(yùn)算符、優(yōu)先級(jí)和函數(shù)。2.宏定義里面的算術(shù)表達(dá)式里面的各個(gè)參數(shù)需要加括號(hào),整個(gè)表達(dá)式本身也需要加括號(hào)。Sizeof問題Sizeof是一個(gè)編譯時(shí)處理的操作符,sizeof最常見的問題就是混淆了結(jié)構(gòu)的體積和結(jié)構(gòu)指針的體積。structtheNode{ inta; charb[20];}Node;...intx=0;structNode*pstNode;x=sizeof(Node);//x=24x=sizeof(pstNode);//x=4建議與結(jié)論:1.sizeof是編譯器在編譯時(shí)處理的,而不是在程序運(yùn)行時(shí)處理的2.結(jié)構(gòu)指針的體積與結(jié)構(gòu)體的體積是兩回事,在32位機(jī)上,指針一般都是32位長(zhǎng)的(即4字節(jié)),而結(jié)構(gòu)體的長(zhǎng)度則依賴于結(jié)構(gòu)體定義structtheNode{ intb[5]; shortc;}Node;sizeof(Node)=?另一個(gè)常見的錯(cuò)誤是某些結(jié)構(gòu)體定義沒有正確使用#pragmapack,導(dǎo)致結(jié)構(gòu)體體積的計(jì)算與理想有偏差。#pragmapack(1)structtheNode{ intb[5]; shortc;}Node;sizeof(Node)=?建議和結(jié)論:1.對(duì)齊有利于提高存儲(chǔ)效率,常見的系統(tǒng)一般默認(rèn)4字節(jié)或8字節(jié)對(duì)齊,編譯時(shí)編譯器將選取系統(tǒng)對(duì)齊和本結(jié)構(gòu)中最常基礎(chǔ)結(jié)構(gòu)二者中的較小值作為該結(jié)構(gòu)的實(shí)際對(duì)齊值。2.這個(gè)錯(cuò)誤經(jīng)常發(fā)生在定義報(bào)文結(jié)構(gòu)是時(shí),報(bào)文結(jié)構(gòu)一般都應(yīng)該按pack(1)來定義。變量的錯(cuò)誤變量的類型和存儲(chǔ)全局變量局部變量全局變量:定義在任何函數(shù)的外部,生命周期是在整個(gè)程序的周期內(nèi)。-定義時(shí)沒有初始化,或者初始化為0的全局變量存放在bss段(對(duì)于bss段,操作系統(tǒng)在加載時(shí)會(huì)自動(dòng)全部清0)-定義時(shí)初始化為非0的全局變量存放在data段局部變量:定義在函數(shù)內(nèi),只能在所在函數(shù)內(nèi)訪問-靜態(tài)局部變量存放在全局堆中,生命周期是在整個(gè)程序-普通局部變量存放在棧中,生命周期是在函數(shù)內(nèi)注意:不管什么變量都要注意初始化問題,變量不初始化而直接作為右值使用是一個(gè)常犯的錯(cuò)誤。全局變量在定義是初始化,會(huì)使app文件增大。因此,對(duì)于全局的大數(shù)組,應(yīng)該盡量避免在定義是初始化,可以在程序執(zhí)行的初始化階段進(jìn)行初始化。intarray[1000][1000]={1};intmain(intargc,char*argv[]){ return0;}用VC編出來的app大小大約為180Kintarray[1000][1000];intmain(intargc,char*argv[]){ return0;}用VC編出來的app大小大約為4.75M建議和結(jié)論:1.盡量避免對(duì)大的全局變量在定義時(shí)進(jìn)行初始化,這樣可以減小app大小,節(jié)省存儲(chǔ)空間2.無論初始化與否,全局變量總是要占用運(yùn)行時(shí)的內(nèi)存空間,因此要避免定義不必要的大型全局變量普遍局部變量是存放在當(dāng)前任務(wù)或系統(tǒng)棧中,因此避免定義過大的局部變量,從而使堆棧溢出。intmain(intargc,char*argv[]){ intarray[1000][1000]={0}; return0;}有什么問題?示例:intfunc1(){ inta[4000]; func2(0); ...

}intfunc1(){ intb[4000]; func3(0); ...}錯(cuò)誤后果:1.在嵌入式設(shè)備上,每個(gè)任務(wù)的棧大小是有限的,一般在任務(wù)創(chuàng)建是指定(一般是4-40k),一旦發(fā)生??臻g溢出,容易造成棧被寫壞,系統(tǒng)死機(jī)2.變量超大等錯(cuò)誤無法通過編譯等手段發(fā)現(xiàn)3.一旦發(fā)生棧被寫壞,則函數(shù)調(diào)用棧也已被破壞,使得問題難以定位。建議和結(jié)論:1.編寫代碼時(shí)不要定義大的局部變量,如果必須要使用大的內(nèi)存,則可以通過malloc從堆中申請(qǐng)。2.使用局部數(shù)組時(shí),要謹(jǐn)防寫越界。3.如果發(fā)生調(diào)用棧損壞,可以從局部變量超大和局部數(shù)組寫越界這個(gè)思路開始追查,看看問題出現(xiàn)時(shí),可能發(fā)生的調(diào)用棧。示例:char*func1(){ chararr[200]; strcpy(arr,"abcd"); returnarr;}錯(cuò)誤后果:1.字符串的內(nèi)存賦值到棧中,這個(gè)空間在func1返回后就不再有意義2.如果func1返回后繼續(xù)向arr這個(gè)地址寫入內(nèi)容,則會(huì)造成棧寫壞,可能導(dǎo)致系統(tǒng)崩潰建議與結(jié)論:1.局部變量一定不要超越其作用域的范圍2.編碼時(shí),在函數(shù)返回指針時(shí)要特別注意,千萬不要返回??臻g地址。思考:下面程序有什么問題?structQueueglobal_q;/*定義一個(gè)隊(duì)列*/...voidospf_routing_calc(){ structQueueNoden; clean_queue(global_q);/*清空隊(duì)列*/ ... EnQueue(global_q,n);/*入隊(duì)列*/ ... if(err)/*出錯(cuò)返回*/ { return } clean_queue(global_q);/*清空隊(duì)列*/ return;/*計(jì)算成功返回*/}建議與結(jié)論:1.局部變量一定不要超出其作用域的范圍2.要特別關(guān)注函數(shù)中異常分支的處理,看看異常分支中有沒有進(jìn)行必要的資源回收和回退處理。數(shù)組和指針的錯(cuò)誤訪問越界指針釋放錯(cuò)誤指針移位錯(cuò)誤數(shù)組和指針的混用訪問越界數(shù)組和內(nèi)存的訪問越界是一類最常發(fā)生的錯(cuò)誤,這類錯(cuò)誤一旦發(fā)生,經(jīng)常會(huì)引起內(nèi)存鏈損壞、調(diào)用棧寫壞等嚴(yán)重后果常見問題和建議:1.字符緩沖區(qū):在進(jìn)行字符串操作時(shí)(strcpy、strcat等),要確保目的緩沖區(qū)的大小足夠大.(包括后面的‘\0’)2.報(bào)文緩沖區(qū):需要考慮各種報(bào)文長(zhǎng)度,如正常報(bào)文、畸形報(bào)文、非法報(bào)文等3.數(shù)組定義:數(shù)組大小已變化,而引用的地方?jīng)]有作相應(yīng)修改,引起訪問越界。(一般是魔鬼數(shù)字導(dǎo)致修改不全,建議用宏來標(biāo)識(shí)數(shù)組大小)4.數(shù)組下標(biāo):檢查數(shù)組下標(biāo)的合法性,防止下標(biāo)過大導(dǎo)致數(shù)組訪問越界。5.字符串的’\0’結(jié)尾:定義字符串?dāng)?shù)組時(shí)忘記后面的’\0’,是一種常見錯(cuò)誤。如charstr[3]="abc";指針釋放錯(cuò)誤指針釋放內(nèi)存錯(cuò)誤是非常大的一類錯(cuò)誤,一代代的C程序員絞盡腦汁地同這些錯(cuò)誤作斗爭(zhēng),在消滅錯(cuò)誤的同時(shí),他們也在不斷創(chuàng)造新的錯(cuò)誤!最簡(jiǎn)單的一類錯(cuò)誤就是遺漏指針釋放,導(dǎo)致內(nèi)存泄漏。主要原因:1.異常處理分支、多個(gè)處理分支中遺漏內(nèi)存釋放和相關(guān)的資源回收處理。建議:a:關(guān)注各個(gè)分支的資源釋放處理,尤其是新增加一個(gè)分支時(shí)。b:將資源釋放集中處理,整合成一個(gè)流程。2.責(zé)任主體不清,接口設(shè)計(jì)沒有明確釋放主體a:設(shè)計(jì)定義接口時(shí),要明確定義資源的申請(qǐng)者和釋放者。引用已釋放的指針也是最常見的一類錯(cuò)誤。如:free(pIntf);printf("freeinterface%s",pIntf->name);建議與結(jié)論:1.即使是剛釋放的內(nèi)存,也不能再訪問,因?yàn)槔锩娴膬?nèi)容已經(jīng)沒有意義了。2.養(yǎng)成釋放內(nèi)存后,立即將指針設(shè)成NULL的良好習(xí)慣。指針賦值除了容易造成內(nèi)存釋放后再訪問的問題外,還容易導(dǎo)致內(nèi)存重復(fù)釋放。pRoute->pIntf=pIntf;...free(pIntf);...if(NULL!=pRoute->pIntf){ ... theID=pRoute->pIntf->theID;/*錯(cuò)誤訪問*/ free(pRoute->pIntf)/*重復(fù)釋放*/ ...}建議與結(jié)論:1.對(duì)于等價(jià)指針的情況,必須要嚴(yán)格明確申請(qǐng)者和釋放者。2.在釋放的時(shí)候要將所有的等價(jià)指針設(shè)成NULL指針移位錯(cuò)誤指針可以通過加減運(yùn)算進(jìn)行推移。需要注意的是,指針的加、減運(yùn)算都是基于它所指向的對(duì)象尺寸大小進(jìn)行考慮的,常見的錯(cuò)誤就是額外的計(jì)算了數(shù)據(jù)類型的體積。示例:intarr[100];int*p=arr;p=p+sizeof(int);/*錯(cuò)誤*/p=p+1;/*正確*/建議和結(jié)論:1.指針偏移時(shí),編譯器已經(jīng)考慮了對(duì)象的體積,編程者不需要再畫蛇添足。2.void型指針由于編譯器不知道其對(duì)象體積,所以不能進(jìn)行加、減偏移運(yùn)算指針和數(shù)組的混用指針和數(shù)組有相同之處,但是在絕大多數(shù)情況下二者含義是不同的,不可混淆。char*b="abc";。chara[4]="abc";1.給指針賦值為數(shù)組首地址,可以通過推移指針來訪問數(shù)組各元素chara[4];char*p=a;通過a[1]和*(p+1)都可以正確訪問數(shù)組2.函數(shù)使用數(shù)組作為參數(shù)時(shí),數(shù)組地址只能以指針的方式傳入,此時(shí)通過數(shù)組或指針的方式定義都是可以的。chara[4];intfunc(char*p){ ...}...ret=func(a);char*p=a;流程和邏輯的錯(cuò)誤統(tǒng)計(jì)和計(jì)數(shù)的錯(cuò)誤任務(wù)切換統(tǒng)計(jì)和計(jì)數(shù)錯(cuò)誤統(tǒng)計(jì)包括很多,如報(bào)文數(shù)目統(tǒng)計(jì)、錯(cuò)誤統(tǒng)計(jì)、各種表項(xiàng)統(tǒng)計(jì)等。統(tǒng)計(jì)計(jì)數(shù)的常見錯(cuò)誤一般有以下幾種情況:1.統(tǒng)計(jì)計(jì)數(shù)變量沒有初始化。2.多個(gè)分支時(shí),某些分支中遺漏統(tǒng)計(jì)。3.統(tǒng)計(jì)計(jì)數(shù)變量溢出統(tǒng)計(jì)計(jì)數(shù)錯(cuò)誤引起的問題大多不太嚴(yán)重,但有時(shí)也可能導(dǎo)致嚴(yán)重的問題。一個(gè)真實(shí)的案例:某一子系統(tǒng)采用一個(gè)LONG型計(jì)數(shù)器記錄系統(tǒng)啟動(dòng)到當(dāng)前的毫秒數(shù),并以此進(jìn)行定時(shí)器調(diào)度,不幸的是,這個(gè)計(jì)數(shù)器沒有進(jìn)行溢出保護(hù),也就是說0x7FFFFFFF/1000/3600/24=24.8天后,這個(gè)計(jì)數(shù)器將發(fā)生溢出。后果:這個(gè)設(shè)備在實(shí)驗(yàn)室從來沒有這么長(zhǎng)時(shí)間運(yùn)行過,問題一直沒有被發(fā)現(xiàn);結(jié)果,一次開局,路由器在網(wǎng)上運(yùn)行了二十多天,計(jì)數(shù)器發(fā)生溢出,定時(shí)器系統(tǒng)崩潰,系統(tǒng)重啟。由于沒有調(diào)用棧,重啟時(shí)沒有任何操作,問題很難定位;直到又過了二十多天,問題再次出現(xiàn),研發(fā)人員發(fā)現(xiàn)間隔的時(shí)間驚人的一致,才發(fā)現(xiàn)了這個(gè)問題。建議與結(jié)論:1.統(tǒng)計(jì)計(jì)數(shù)雖然簡(jiǎn)單,但也不可以掉以輕心,分支流程容易遺漏,需要特別關(guān)注。2.對(duì)于計(jì)數(shù)器一定要考慮什么時(shí)候會(huì)溢出,以及溢出時(shí)的保護(hù)處理。任務(wù)切換1.在多任務(wù)編程時(shí),需要考慮任務(wù)切換時(shí)對(duì)全局資源的保護(hù)處理。2.如果多個(gè)任務(wù)間有嚴(yán)格的時(shí)序要求,則需要保證各個(gè)任務(wù)間的同步,防止亂序而導(dǎo)致意想不到的結(jié)果。C語言程序設(shè)計(jì)常見問題1.下面代碼有什么問題?當(dāng)我試圖訪問p2是得到了錯(cuò)誤,為什么?char*p1,p2;p2=(char*)0x80000000;作者的原因是定義兩個(gè)char型的指針,但是上述代碼實(shí)際上等價(jià)于:char*p1,p2;或者char*p1;charp2;因此作者在后面的指針賦值語句時(shí)會(huì)產(chǎn)生錯(cuò)誤正確的定義法是:char*p1,*p2;2.C語言的關(guān)鍵字extern在函數(shù)的聲明中起到什么作用?如:externintfunc(int);如果函數(shù)的聲明中帶有關(guān)鍵字extern,除了暗示這個(gè)函數(shù)可能在其他源文件里定義外,無其他作用。下面兩個(gè)函數(shù)的聲明沒有明顯區(qū)別:externintfunc(int);intfunc(int);3.下面兩種對(duì)于定義string_t數(shù)據(jù)類型的方法,哪一種更好?typedefchar*string_t#definestring_tchar*通常講,typedef要比#define好,尤其在對(duì)指針的處理上。示例:typedefchar*string_t1#definestring_t2char*string_t1s1,s2;string_t2s3,s4上述變量s1、s2、s3都被定義成了char*,而s4被定義成了char,而不是預(yù)期的char*。根本原因就是#define只是進(jìn)行字符串的簡(jiǎn)單替換。除此之外,宏定義有#ifdef和#ifndef等用來進(jìn)行邏輯判斷,這是它的長(zhǎng)處。4.typedef中的嵌套定義問題。下面定義有問題嗎?typedefstructmystruct{ inta; MY_STRUCTURE*pnext;}MY_STRUCTURE;規(guī)范的做法是:typedefstructmystruct{ inta; structmystruct*pnext;};typedefstructmystructMY_STRUCTURE5.下面的方法定義數(shù)組有問題嗎?constinta=5;intarray[a];這個(gè)問題討論的是常量與只讀變量的區(qū)別。常量,如5、“abc”等肯定是只讀的,因?yàn)槌绦蛑懈緵]有地方存放它們的值,別說修改它了。而只讀變量,則是在內(nèi)存中開辟一個(gè)地方來存儲(chǔ)它的值,只不過這個(gè)值不允許修改。C語言的const就是用來限定一個(gè)變量不允許修改的修飾符。上述代碼的a被修飾為只讀變量,但它本質(zhì)上還是變量,而不是常量。C語言規(guī)定,數(shù)組的定義必須是常量,而不能是只讀變量。6.下面的代碼中編譯器會(huì)報(bào)一個(gè)錯(cuò)誤,為什么?typedefchar*charptr;charstr[4]="abc";constchar*p1=str;constcharptrp2=str;p1++;p2++;p2++有問題,會(huì)報(bào)錯(cuò)。因?yàn)閏onstcharptrp2與#define不同,不是進(jìn)行簡(jiǎn)單的字符串替換,它實(shí)際上定義的是一個(gè)常指針,即指針初始化后不允許改變。類似于constlongxyz;只不過charptr是我們自己定義的類型。7.為什么結(jié)構(gòu)體變量不能用“==”和“!=”進(jìn)行比較?C語言是一種低級(jí)語言,沒有一種簡(jiǎn)單有效的辦法實(shí)現(xiàn)結(jié)構(gòu)體變量的比較。具體原因有兩個(gè):1)結(jié)構(gòu)體對(duì)齊問題,導(dǎo)致的一些填充域,這些填充域可能是隨機(jī)值,因此按字節(jié)比較;2)如果按域比較,結(jié)構(gòu)體中若含有指針域,則指針域所指內(nèi)容的比較則無法實(shí)現(xiàn)。因此結(jié)構(gòu)體的比較往往需要各應(yīng)用模塊根據(jù)自己的需要專門寫一個(gè)比較函數(shù)8.如何初始化一個(gè)聯(lián)合體的任意成員?typedefunionmyunion{ intx; shorty;}UN;UNux={4,1};//很遺憾,標(biāo)準(zhǔn)C不支持初始化union的任意成員,而只能初始化它的第一個(gè)成員。9.下面代碼能告訴我們b在a和c之間嗎?if(a<b<c){ ...}上述代碼實(shí)際等價(jià)于if((a<b)<c){ ...}意思是,取(a<b)判斷的邏輯結(jié)果,然后再和c比大小。10.C語言的指針很重要,也很靈活,不過它到底有哪些好處?請(qǐng)列舉1.方便使用動(dòng)態(tài)分配的數(shù)組2.對(duì)相同類型或相似類型的多個(gè)變量進(jìn)行通用訪問3.變相改變函數(shù)的只傳遞特性,如將變量地址作為參數(shù)傳入函數(shù),這樣就可以修改該變量的值4.動(dòng)態(tài)擴(kuò)展數(shù)據(jù)結(jié)構(gòu),如鏈表、hash表5.遍歷數(shù)組6.節(jié)省函數(shù)調(diào)用代價(jià),將參數(shù)尤其是大個(gè)的參數(shù),按指針傳遞,以減少開銷。7.…………11.下面的代碼打印出來的結(jié)果是多少?inti,array[5],*ip;ip=array;for(i=0;i<5;i++){ array[i]=i;}printf("\r\n%d",*(ip+3*sizeof(int)));呵呵。。,具體打印什么我也不清楚。問題就出在ip+3*sizeof(int)上了。指針的相加實(shí)際上已經(jīng)考慮了其類型的長(zhǎng)度。示例:如果ip所指向的地址為0x80000000,則ip+1所指的地址0x80000004。因此本題中不需要再乘以sizeof(int)了。12.*p++到底是給誰加1?是給指針p還是p所指的內(nèi)容加1?單目操作符和操作的結(jié)合順序是:從右到左一個(gè)表達(dá)式中單目運(yùn)算符的執(zhí)行順序是:從左到右因此本題中實(shí)際上是對(duì)指針p加1,而不是p的內(nèi)容加1,如果要對(duì)p的內(nèi)容加1,則用下面的表達(dá)式:(*p)++13.我有一個(gè)char*類型的指針,恰好指向一個(gè)int型,我想讓指針跳過這個(gè)int,跳到下一個(gè)char,試問下面代碼能否實(shí)現(xiàn)?(int*)p++;這種實(shí)現(xiàn)標(biāo)準(zhǔn)C不支持,但是主流編譯器默認(rèn)都會(huì)支持,如VC和gcc。雖然這么用一般不會(huì)有什么問題,不過還是建議大家不要這么用,上面問題可以直接用下面代碼替代:p+=sizeof(int);14.為什么我不能對(duì)void*型的指針進(jìn)行算術(shù)運(yùn)算?如:void*p=array;p+=5;前面說過,對(duì)指針進(jìn)行算術(shù)運(yùn)算(指針加、減運(yùn)算)時(shí)實(shí)際上已經(jīng)考慮了該指針?biāo)割愋偷拇笮×恕M?,如果無法知道該指針類型的大小,則無法進(jìn)行指針的算術(shù)運(yùn)算。因?yàn)榫幾g器根本不知道你這個(gè)指針?biāo)傅淖兞空紟讉€(gè)字節(jié),也就無法進(jìn)行指針偏移。15.有些頭文件中將NULL定義為0,為什么?NULL用于表示指針為空指針;一般用于指針變量的初始化。NULL的具體的機(jī)器表示也隨機(jī)器而定,不過一般都是0.16.在源文件里定義了一個(gè)數(shù)組:intarray[5];我能否在另一個(gè)文件里聲明一個(gè)指針,從而引用這個(gè)數(shù)組?externint*array;不可以,程序運(yùn)行時(shí)會(huì)告訴你非法訪問!指向類型T的指針并不等價(jià)于類型T的數(shù)組。正確的用法是:externintarray[];17.有人說數(shù)組名無法賦值,但是下面程序確實(shí)可以工作,難道我記錯(cuò)了?intfun(charsz[100]){ ... if('\0'==sz[0]) { sz=NONE; } ...}在C語言中,數(shù)組無法真正傳遞給函數(shù),因而在編譯器內(nèi)部這個(gè)函數(shù)就被解釋為:intfun(char*sz)因此,C語言函數(shù)參數(shù)若是數(shù)組,則實(shí)際傳遞的是一個(gè)指針,也就是該數(shù)組的首地址。因此上述函數(shù)當(dāng)然沒有問題。18.假定有一個(gè)整型數(shù)組a,則a和&a有什么區(qū)別?inta[2];a和&a有什么區(qū)別?a是指向數(shù)組第一個(gè)元素的指針,而&a則是指向整個(gè)數(shù)組的指針。intx,a[2];int*p=NULL;p=a;x=*p;p=&a;19.什么時(shí)候需要定義指向數(shù)組的指針而不是數(shù)組元素的指針?如何定義?intarray[2][3]={{1,2,3},{4,5,6}};如果需要對(duì)行進(jìn)行遍歷的話,就需要一個(gè)指向行的指針,定義如下:

intarray[2][3]={{1,2,3},{4,5,6}}; int(*p)[3]=0; //int*p2=0; p=&array[1]; //p2=array[1];20.有人寫了一個(gè)將整數(shù)轉(zhuǎn)換成字符串的函數(shù),char*itoa(intn){ charbuf[20]; sprintf(buf,"%d",n); returnbuf;}如果按下面調(diào)用,有什么問題? char*p3; p3=itoa(5); { intc; c=1; } printf("%s",p3);char*itoa(intn)函數(shù)返回了??臻g的地址,要知道,??臻g的內(nèi)容在該函數(shù)返回后就不再受到保護(hù),也就是說此時(shí)函數(shù)的??臻g的已經(jīng)被系統(tǒng)回收。如果函數(shù)返回了??臻g的內(nèi)存,則很容易出現(xiàn)錯(cuò)誤的結(jié)果,因?yàn)樵摽臻g可能已經(jīng)分配給了其他函數(shù)或任務(wù)。21.為什么有些代碼中malloc申請(qǐng)內(nèi)存時(shí)的返回值總是強(qiáng)制類型轉(zhuǎn)換一下,不轉(zhuǎn)行不行?char*p=(char*)malloc(100);在C語言引入void*指針類型之前,這種強(qiáng)制轉(zhuǎn)換主要是為了消除編譯告警。在C語言引入了void*指針類型之后,這種轉(zhuǎn)換已經(jīng)沒有必要,但是這是一種良好的編程習(xí)慣,建議大家繼續(xù)保持這種強(qiáng)制轉(zhuǎn)換,增加代碼可讀性。22.下面的程序有什么問題?#definePI3.14intfit_size(doublerount){ if((rount/PI==10)||(rount/PI==20)) { return1; } return0}此題同樣是浮點(diǎn)數(shù)比較問題。浮點(diǎn)數(shù)不能直接與某一個(gè)整數(shù)進(jìn)行比較,而是應(yīng)該和一個(gè)范圍比較。上面程序應(yīng)該改成:#definePI3.14intfit_size(doublerount){ if((fabs(rount/PI-10)<0.000001)||(fabs(rount/PI-20)<0.000001)) { return1; } return0}23.下面這樣的寫法正確嗎?intfun(inta){ intk=0; switch(a) { default: k=1; break; case1: k=2; break; case2: k=3; break; } return0}這種寫法正確,因?yàn)閟witch……case

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論