版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
指針與內(nèi)存管理靜態(tài)/全局內(nèi)存靜態(tài)聲明的變量分配在這里全局變量也使用這部分內(nèi)存這些變量在程序開始運行時分配,直到程序終止才消失,所有函數(shù)都能訪問全局變量,靜態(tài)變量的作用域局限在定義他們的函數(shù)內(nèi)部。自動內(nèi)存這些變量在函數(shù)內(nèi)聲明,并且在函數(shù)被調(diào)用時才創(chuàng)建,它們的作用域局限于函數(shù)內(nèi)部,而且生命周期限制在函數(shù)的執(zhí)行時間內(nèi)。動態(tài)內(nèi)存內(nèi)存分配在堆上,可以根據(jù)需要釋放,而且直到釋放才消失指針引用分配的內(nèi)存,作用域局限于引用內(nèi)存的指針。指針變量包含內(nèi)存中別的變量、對象或函數(shù)的地址。指針本身并沒有包含所引用數(shù)據(jù)的類型信息,只包含地址。示例1:同為打印指針數(shù)組第二個元素第二個字母,但數(shù)組方式表達更簡潔。char*names[]={"MillerVJones","Anderson"};printf("%c\n",*(*(names+1)+2));//這里需把names看作二級指針,即names存放的是字符串數(shù)組的首地址,而*names內(nèi)容為指針數(shù)組第一個元素的首地址。printf("%c\n”,names[1][2]);使用指針可能出現(xiàn)的問題:訪問數(shù)組和其他數(shù)據(jù)結(jié)構(gòu)時越界自動變量消失后被引用堆上的內(nèi)存釋放后被引用內(nèi)存分配之前解引指針注:1.若指向未初始化的指針,指針內(nèi)容可能并不是一個合法的地址,就算是合法地址,也可能沒包含合法的數(shù)據(jù),程序沒有權(quán)限訪問不合法地址。不初始化也可以使用指針,但盡快初始化指針是好習慣如何區(qū)分常量指針和指針常量:答案是倒過來讀(理解),如constint*p;int*constp;//倒過來讀很好區(qū)分注:1.整型不能賦值給指針,但可以賦值為0,如:int*p=0,原因是0被重載了虛擬內(nèi)存和指針程序使用的是虛擬內(nèi)存,操作系統(tǒng)會在需要時把虛擬地址映射到物理內(nèi)存地址。Constint*const*p為雙重指針,即指向“指向常量的常量指針”左值:指的是賦值操作符左邊的操作數(shù),所有的左值都必須可以修改,因為它們會被賦值。Null的概念:NULL的定義:#defineNULL((void*)0)Void指針:通用指針,用來存放任何數(shù)據(jù)類型的指針,但只能用作數(shù)據(jù)指針,不能用作函數(shù)指針。注:l.void指針具有與char指針相同的形式和內(nèi)存對齊方式void指針和別的指針永遠不會相等,不過兩個賦值為NULL的void指針相等。sizeof可以用在void*上,不能用在void上全局和靜態(tài)指針:指針被聲明為全局或靜態(tài),就會在程序啟動時被初始化為NULL。存放在堆中。指針的算術(shù)運算:指針加法:給指針加上一個整數(shù),實際上加的數(shù)是這個整數(shù)和指針數(shù)據(jù)類型對應(yīng)字節(jié)數(shù)的乘積示例2:intvector[]=(28,41,7};Int*pi=vector;//若pi=100Printf("%d\n”,*pi);輸出28Pi+=1;//pi=104Printf("%d\n”,*pi);//輸出41注:a.對于short類型的指針,加1時實際上地址值加2,char型則加1指針減法和加法運算一樣兩指針可以相減,也可以進行比較,但通常沒什么用,可以用來判斷數(shù)組中元素的順序。Malloc動態(tài)內(nèi)存分配:示例3:int*pi=(int*)malloc(sizeof(int));*pi=5;printf("%d\n”,*pi);free(pi);注:malloc和free必須成對使用,指針釋放過后就不應(yīng)該再去訪問,通常將釋放后的指針賦值為NULL若分配的內(nèi)存沒有釋放則會造成內(nèi)存泄漏靜態(tài)、全局指針和malloc:初始化靜態(tài)或全局變量時不能調(diào)用函數(shù),如下面這句就會報錯。Staticint*pi=malloc(sizeof(int));對于靜態(tài)變量,可以通過在后面用一個單獨的語句給變量分配內(nèi)存來避免這個問題。如:Staticint*pi;Pi=malloc(sizeof(int));但是全局變量不能用單獨的賦值語句,因為全局變量是在函數(shù)和可執(zhí)行代碼外部聲明的,而賦值語句這類代碼必須出現(xiàn)在函數(shù)中注:在編譯器看來,作為初始化操作符的=和作為賦值操作符的=不一樣。使用calloc函數(shù):Calloc會在分配內(nèi)存的同時清空內(nèi)存。Int*pi=calloc(5,sizeof(int));//第一個參數(shù)為數(shù)量,第二個參數(shù)為類型占用字節(jié)數(shù)迷途指針:如果內(nèi)存已經(jīng)釋放,而指針還在引用原始內(nèi)存。這樣的指針就叫做迷途指針。注:visualstudio會在釋放內(nèi)存后用0xCC、0xCD或者0xDD復寫數(shù)據(jù)。在不拋出異常的情況下,如果在預期之外的地方看到這些值,可以認為程序可能在訪問已經(jīng)釋放的內(nèi)存。程序棧:是支持函數(shù)執(zhí)行的內(nèi)存區(qū)域,通常和堆共存,也就是說它們同享同一塊內(nèi)存區(qū)域,程序棧通常占據(jù)這塊區(qū)域的下部而堆則占用上部。程序棧存放棧幀棧幀存放函數(shù)參數(shù)和局部變量,堆管理動態(tài)內(nèi)存。調(diào)用函數(shù)時,會創(chuàng)建函數(shù)的棧幀并將其推到程序棧上,函數(shù)返回時,其棧幀從程序棧彈出。系統(tǒng)在創(chuàng)建棧幀時,將參數(shù)以跟聲明相反的順序推到幀上,最后推入局部變量。函數(shù)中的塊語句被當作“微函數(shù)”,會在合適的時機(執(zhí)行時)被推入棧和從棧中彈出。通過指針傳遞和返回數(shù)據(jù):傳遞指針可以讓多個函數(shù)訪問指針所引用的對象,而不用把對象聲明為全局可訪問。用指針傳遞數(shù)據(jù)的一個主要原因是函數(shù)可以修改數(shù)據(jù)。通過傳遞一個指向常量的指針可以使用指針傳遞數(shù)據(jù)并禁止其被修改。而當數(shù)據(jù)是需要被修改的指針時就傳遞指針的指針(兩級指針,注:用值傳遞數(shù)據(jù)時,不能改變實參的值,因為修改形參不會影響實參。返回指針可能存在的問題:返回未初始化的指針返回指向無效地址的指針返回局部變量的指針返回指針但沒有釋放內(nèi)存(使用malloc在函數(shù)內(nèi)部分配內(nèi)存并返回地址,調(diào)用者負責釋放返回的內(nèi)存。)傳遞指針的指針:將指針傳遞給函數(shù)時,傳遞的是值。如果想要修改原指針而不是指針的副本,就需要傳遞指針的指針。同傳遞值一樣,只傳遞一個指針是不能改變其值的,相當于改變了形參。示例4:voidallocArray(int**arr,intsize,intvalue)(*arr=(int*)malloc(size*sizeof(int));If(*arr!=NULL)(for(inti=0;i<size;i++)(*(*arr+1)=value;}}}Int*vector=NULL;allocArray(&vector,5,45);函數(shù)指針:指持有函數(shù)地址的指針,這為我們以編譯時未確定的順序執(zhí)行函數(shù)提供了一種選擇,而不需要使用條件語句。聲明示例:void(*fo)(int);調(diào)用時直接將函數(shù)名傳遞給函數(shù)指針。傳遞函數(shù)指針示例:示例5:intadd(intnum1,intnum2)(returnnum1+num2;}Intsubstract(intnum1,intnum2)(returnnum1-num2;}Typedefint(*fptrOperation)(int,int);Intcompute(fptrOperationoperation,intnum1,intnum2)(returnoperation(num1,num2)}Printf("%d\n”,compute(add,5,6));Printf("%d\n”,compute(substract,5,6);返回函數(shù)指針:返回函數(shù)指針需要把函數(shù)的返回類型聲明為函數(shù)指針。示例6:fptrOperationselect(charopcode)(switch(opcode)(case‘+’:returnadd;Case‘-‘:returnsubstract;}}Intevaluate(charopcode,intnum1,intnum2)(fptrOperationoperation=select(opcode);Returnoperation(num1,num2);Printf("%d\n”,evaluate(‘+’,5,6));Printf("%d\n,evaluate(‘-‘,5,6));指針和數(shù)組:數(shù)組和指針表示法緊密關(guān)聯(lián),在合適的上下文可以互換。盡管數(shù)組名字有時候可以當作指針來使用,但數(shù)組的名字不是指針。數(shù)組表示法和指針可以一起使用,但兩者明顯不同也不一定能互換,比如說,盡管數(shù)組使用自身的名字可以返回數(shù)組地址,但是名字本身不能作為賦值操作的目標。注:1.數(shù)組是能用索引訪問的同質(zhì)元素連續(xù)集合,連續(xù)是指數(shù)組中的元素在內(nèi)存中是相鄰的,中間不存在空隙??梢詫⒍S數(shù)組當作數(shù)組的數(shù)組,即,如果只用一個下標訪問數(shù)組,得到的是對應(yīng)行的指針。使用sizeof得到的是整行的長度。將數(shù)組名作為地址賦值給指針,是把數(shù)組第一個元素的地址賦值給指針,即,指針指向數(shù)組的第一個元素而不是數(shù)組本身。Sizeof對數(shù)組和同一數(shù)組的指針操作是不同的,前者返回整個數(shù)組的大小,后者返回指針本身的大小。指針是一個左值,能修改,而數(shù)組名字不能被修改。如果從堆上(malloc)分配內(nèi)存,并把地址賦給一個指針,那就可以對指針使用數(shù)組下標,并把這塊內(nèi)存當作一個數(shù)組。不過用完記得釋放。指針數(shù)組和數(shù)組指針:Int*arr[5];和int(*arr)[5];是不同的,前者為指針數(shù)組,數(shù)組的每一個元素都為一個指針,存放的是地址值。后者為數(shù)組指針,arr為一個指向二維數(shù)組的指針,該二維數(shù)組的元素都是整數(shù),且每行有5個元素,可以類比于函數(shù)指針,需要將二維數(shù)組的地址傳給arr。示例7:intmatrix[2][5]={{1,2,3,4,5},{6,7,8,9,10}};Int(*ptrmatrix)[5]=matrix;注:這里matrix代表的是二維數(shù)組的首地址,matrix[0]也代表首地址,但matrix+1和matrix[0]+1代表的地址不同。前者表示加了20,后者表示加了4.傳遞多維數(shù)組:給函數(shù)傳遞多維數(shù)組時,要決定在函數(shù)原型聲明中使用數(shù)組表示法還是指針表示法,還要考慮如何傳遞數(shù)組的形態(tài),這里的形態(tài)指數(shù)組的維數(shù)及每一維的大小。示例8:voiddisplay2DArray(intarr[][5],introws){};或者voiddisplay2DArray(int(*arr)[5],introws){};調(diào)用時,display2DArray(matrix,2);注:函數(shù)不會為這個數(shù)組分配內(nèi)存,因為傳遞的是地址。動態(tài)分配二維數(shù)組:像上面的matrix,其聲明方式?jīng)Q定了其所分配的內(nèi)存是連續(xù)的,但使用malloc這樣的函數(shù)創(chuàng)建二維數(shù)組時,分配的內(nèi)存可能不連續(xù)。示例9:introws=2;Intcols=5;Int**matrix=(int**)malloc(rows*sizeof(int*));For(inti=0;i<rows;i++)(matrix[i]=(int*)malloc(cols*sizeof(int));}示例9這種方式分配的內(nèi)存就不連續(xù)。示例10:introws=2;Intcols=5;Int**matrix=(int**)malloc(rows*sizeof(int*));Matrix[0]=(int*)malloc(rows*cols*sizeof(int));For(inti=1;i<rows;i++)(matrix[i]=matrix[0]+cols*I;}示例10這種方式分配的就是連續(xù)的內(nèi)存。指針與字符串字符串是以ASCII字符NUL(\0)結(jié)尾的字符序列,通常存儲在數(shù)組或從堆上分配的內(nèi)存中。字符串有兩種類型,一種是由char數(shù)據(jù)類型組成的單字節(jié)字符串,一種是由wchar_t數(shù)據(jù)類型組成的寬字符串(16位或32位,主要用于支持非拉丁字符集),處理函數(shù)分別包含在string.h和wchar.h頭文件中。注:1.字符串長度指的是字符串中除了NUL字符之外的字符數(shù),而分配內(nèi)存時也要為NUL字符分配。為了防止字符串被修改,可將其聲明為常量指針,即constchar*p="sound”。指針與結(jié)構(gòu)體結(jié)構(gòu)體的定義使用struct關(guān)鍵字,通常結(jié)構(gòu)體名的前面加上下劃線。,訪問結(jié)構(gòu)體成員用點(?)操作符,當使用結(jié)構(gòu)體指針時,使用箭頭(T)操作符,當然也可以先將指針解引*)后使用點操作符。結(jié)構(gòu)體的聲明通常使用typedef關(guān)鍵字,如下:typedefstruct_person(char*fisrtName;char*lastName;char*title;unsignedintage;}Person;為結(jié)構(gòu)體分配內(nèi)存為結(jié)構(gòu)體分配內(nèi)存時,分配的內(nèi)存大小至少是各個字段的長度和,不過實際長度通常會大于這個和,因為結(jié)構(gòu)體各字段之間可能會有填充,某些數(shù)據(jù)類型需要對齊到特定邊界就會產(chǎn)生填充。比如說,短整數(shù)通常對齊到能被2整除的地址上,而整數(shù)對齊到能被4整除的地址上。詳解字節(jié)對齊:編譯器默認會對結(jié)構(gòu)體進行字節(jié)對齊處理,目的是加快計算機取數(shù)速度。三準則:結(jié)構(gòu)體變量的首地址能夠被其最寬基本類型(內(nèi)置數(shù)據(jù)類型,如char,int,short,double,float)成員的大小所整除。結(jié)構(gòu)體每個成員相對于結(jié)構(gòu)體首地址偏移量(offset)都是成員大小的整數(shù)倍,如有需要編譯器會在成員之間填充字節(jié)(internalpadding)結(jié)構(gòu)體總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如有需要編譯器會在最末一個成員之后加上填充字節(jié)(trailingpadding)注:1.結(jié)構(gòu)體某個成員相對于結(jié)構(gòu)體首地址的偏移量可以通過offsetof()宏來獲得,如offsetof(_person,firstName);由于結(jié)構(gòu)體成員可以是復合類型,如另一個結(jié)構(gòu)體,所以在尋找最寬基本類型成員時,應(yīng)當包含復合數(shù)據(jù)類型的子成員,而不是看成一個整體。但在確定復合類型的偏移量是則是看作一個整體。示例11:struct_S1{inti;charc;}struct_S2{charc1;_S1s;charc2;}則sizeof(_S2)=4+(4+4)+4=16.//以4的整數(shù)倍對齊再如:struct_S3(inti;charc;shorts;}則sizeof(_S3)=4+2+2=8;〃由規(guī)則1和規(guī)則2可得出可以通過#pragmapack(n)指令調(diào)整結(jié)構(gòu)體對齊方式,其中n為字節(jié)對齊數(shù)(取1,2,4,8,16)示例12:#pragmapack(push)//將當前pack值壓棧保存#pragmapack(2)struct_S1(inti;charc;}struct_S2(charc1;_S1s;charc2;}#pragmapack(pop)//恢復之前的pack值此時sizeof(_S2)=2+(2+4)+2=10??盏慕Y(jié)構(gòu)體大小為1。結(jié)構(gòu)體包含靜態(tài)成員變量時,結(jié)構(gòu)體大小不包括該靜態(tài)變量。結(jié)構(gòu)體位域的概念位域的主要目的是壓縮存儲規(guī)則如下:如果相鄰位域字段的類型相同,且其位寬之和小于數(shù)據(jù)類型的sizeof大小,則后面的字段緊鄰前一個字段存儲,直到不能容納為止。如果相鄰位域字段的類型相同,但其位寬之和大于數(shù)據(jù)類型的sizeof大小,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數(shù)倍。如果相鄰的位域字段類型不同,則不壓縮如過位域指端之間穿插非位域字段,則不進行壓縮。整個結(jié)構(gòu)體的總的寬度為最寬基本數(shù)據(jù)類型的整數(shù)倍如:示例13struct_t(chart1:3;chart2:4;chart3:5;shorts;chart4:3}則sizeof(_t)=1+1+2+2=6指針誤用不恰當?shù)闹羔樎暶?,如int*ptr1,ptr2;實際上只有ptr1為指針,正確的聲明方式應(yīng)為int*ptr1,*ptr2。不過可以可以采用下面的類型定義方式:typedefint*pint;pintptr1,ptr2;注:這里要區(qū)分#define和typedef,前者為宏定義,在編譯預處理時進行簡單的替換,而后者不是簡單的替換,而是采用如同定義變量的方法那樣來聲明一種類型。因此:#definePINTint*PINTptr1,ptr2;〃同樣只有ptr1為指針另外,對于constPINTp;和constpintp;前者為指針常量(指向的內(nèi)容不可變,后則為常量指針(指針本身不可多次賦值。因為pint作為數(shù)據(jù)類型把p鎖定了。使用指針前為初始化,即野指針。使用malloc這類函數(shù)時,一定要檢查返回值,否則可能導致程序非正常終止。示例14:float*vector=(float*)malloc(20*sizeof(float));if(vector==NULL)(}else(}一旦不再需要內(nèi)存中的敏感蘇劇,馬上覆寫是個好主意(使用memset)為了避免重復釋放指針,在釋放過后總是將其置為NULL。轉(zhuǎn)換指針類型轉(zhuǎn)換是一種基本操作,跟指針結(jié)合使用很有用,原因包括:訪問有特殊目的的地址一般發(fā)生在嵌入式系統(tǒng)上,可以把某特殊地址賦值給一個指針,再把某個字符賦值給這個地址。如:#defineVIDEO_BASE0xB8000int*video=(int*)VIDEO_BASE;*video=’A’;分配一個地址來表示端口端口即是硬件概念,也是軟件概念。服務(wù)器用軟件端口指明它們要接收發(fā)給這臺機器的某類消息,硬件端口就是物理端口,程序通過讀寫硬件端口可以處理信息和命令。示例15:#definePORT0xB0000000unsignedintvolatile*constport=(unsignedint*)PORT;注:a.這里的volatile關(guān)鍵字修飾符可以譯為“直接存取原始內(nèi)存地址”,表示可以在程序以外改變變量,比如說,外部設(shè)備可能會向端口寫入數(shù)據(jù),且可以獨立于計算機的處理器執(zhí)行這個寫操作,也有可能在多線程程序中共享同一變量的其它線程中發(fā)生改變的情形。出于優(yōu)化的目的,編譯器有時候會臨時使用緩存或者寄存器來持有內(nèi)存的值,如果外部操作修改了這個內(nèi)存的值,改動不能反映到緩存或者寄存器中。用volatile可以阻止運行時系統(tǒng)使用寄存器暫存端口值,每次訪問端口都需要系統(tǒng)讀寫端口,而不是從寄存器中讀取一個可能已經(jīng)過期的值。但是不應(yīng)該把所有變量都聲明為volatile,這樣會組織編譯器進行所有類型的優(yōu)化。示例16:intsquare(volatileint*ptr)(return*ptr**ptr;}上段代碼的本意是計算ptr指向內(nèi)容的平方,但由于ptr被volatile修飾,可能計算過程中發(fā)生改變,得到的就不是平方值了。又如volatilechara;a=0;while(!a)(//dosomethings;}doother();如果沒有volatiledoother()不會被執(zhí)行b.一個變量可以同時為const和volatile,它是volatile因為
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度綠色能源項目投資定金合同附屬協(xié)議書2篇
- 二零二五年度權(quán)威解讀!欠條法律風險防范及處理合同3篇
- 二零二五年度白酒定制生產(chǎn)與品牌發(fā)展合同2篇
- 二零二五年度高鐵安裝工程設(shè)備磨損保險合同2篇
- 2025年度西餐廳經(jīng)營管理權(quán)租賃合同3篇
- 二零二五年度航空貨運代理航空貨物包裝材料供應(yīng)合同3篇
- 展會展臺拆除合同(2篇)
- 小區(qū)道路工程承包合同(2篇)
- 2025年餐飲食材配送與售后服務(wù)合同協(xié)議3篇
- 二零二五年度航空航天零部件耗材采購合同范本3篇
- 幼兒園反恐防暴技能培訓內(nèi)容
- 食品企業(yè)質(zhì)檢員聘用合同
- 中醫(yī)診所內(nèi)外部審計制度
- 自然辯證法學習通超星期末考試答案章節(jié)答案2024年
- 2024年國家危險化學品經(jīng)營單位安全生產(chǎn)考試題庫(含答案)
- 護理員技能培訓課件
- 家庭年度盤點模板
- 河南省鄭州市2023-2024學年高二上學期期末考試 數(shù)學 含答案
- 2024年資格考試-WSET二級認證考試近5年真題集錦(頻考類試題)帶答案
- 試卷中國電子學會青少年軟件編程等級考試標準python三級練習
- 公益慈善機構(gòu)數(shù)字化轉(zhuǎn)型行業(yè)三年發(fā)展洞察報告
評論
0/150
提交評論