內(nèi)存管理
作用域
一個C語言變量的作用域可以是代碼塊作用域涩赢,函數(shù)作用域或者文件作用域站叼。
代碼塊是{}之間的一段代碼原茅。
auto自動變量
一般情況下代碼塊內(nèi)部定義的變量都是自動變量锦积。當(dāng)然也可以顯示的使用aotu關(guān)鍵字响牛。自動變量自動分配內(nèi)存妓湘,自動釋放內(nèi)存查蓉,這個過程在變量的作用域內(nèi)進行 。
register寄存器變量
通常變量在內(nèi)存當(dāng)中榜贴,如果能把變量放到CPU的寄存器里面豌研,代碼執(zhí)行效率會更高。如:register int I;(只是建議放在寄存器內(nèi)部進行計算)唬党,不能對寄存器變量取地址鹃共,&i這樣是不可以的。
代碼塊作用域的靜態(tài)變量
靜態(tài)變量是指內(nèi)存位置在程序執(zhí)行期間一直不改變的變量驶拱,一個代碼塊內(nèi)部的靜態(tài)變量只能被這個代碼塊內(nèi)部訪問霜浴。如:static int i=0;只初始化一次。
代碼塊作用域外的靜態(tài)變量
代碼塊之外的靜態(tài)變量在程序執(zhí)行期間一直存在蓝纲,但只能被定義這個變量的文件訪問阴孟,整個文件內(nèi)部都是它的作用域。
全局變量
全局變量的存儲方式和靜態(tài)變量相同税迷,全局變量這個內(nèi)存空間一直存在永丝,但可以被多個文件訪問。要在沒有定義全局變量的文件訪問全局變量箭养,要加個關(guān)鍵字extern慕嚷,表示修飾的變量已在別的文件定義好了,可以在現(xiàn)文件中訪問了,全局變量是很危險的喝检,少用嗅辣。
static關(guān)鍵字
加了static,其實就代表了作用域挠说。在C語言中函數(shù)默認(rèn)都是全局的澡谭,所以加了static關(guān)鍵字修飾的函數(shù),那么該函數(shù)的只能在所在文件中被訪問纺涤,變量也一樣 译暂。
外部變量與extern關(guān)鍵字
如:extern int I;代表一個變量在另外的源文件定義好了
內(nèi)存四區(qū)(最基本的四區(qū))
這四個區(qū)是相互獨立的,不可能連續(xù)挨在一起的撩炊,是操作系統(tǒng)分配的(邏輯地址是連續(xù)的外永,物理地址是不連續(xù)的)。
代碼區(qū):代碼區(qū)code拧咳,程序被操作系統(tǒng)加載到內(nèi)存的時候伯顶,所有的可執(zhí)行代碼都加載到代碼區(qū),也叫代碼段骆膝,這塊內(nèi)存是不可以在運行期間修改的祭衩。
靜態(tài)區(qū):所有的全局變量/常量以及程序中的靜態(tài)變量、常量都存儲到靜態(tài)區(qū)阅签。c程序運行期間掐暮,地址一直有效。
棧區(qū):棧stack是一種先進后出的內(nèi)存結(jié)構(gòu)政钟,所有的自動變量路克,函數(shù)的形參都是由編譯器自動放在棧中,當(dāng)一個自動變量超出其作用域時养交,自動從棧中彈出(自動釋放內(nèi)存)精算。對C語言來講,函數(shù)形參是從右到左入棧碎连。
堆區(qū):堆heap和棧一樣灰羽,也是一種在程序運行過程中可以隨時修改的內(nèi)存區(qū)域,但沒有棧那樣先進后出的順序鱼辙。堆是一個大容器廉嚼,它的容量要遠(yuǎn)遠(yuǎn)大于棧,但是在C語言中倒戏,堆內(nèi)存空間的申請和釋放需要手動通過代碼來完成前鹅。
堆的分配與釋放
int *p=(int *)malloc(sizeof(int));在堆里面分配一個int的內(nèi)存。
free(p);在堆當(dāng)中申請的內(nèi)存峭梳,一定要用free釋放。
malloc和free一定要成對出現(xiàn)。
堆葱椭、棧和內(nèi)存映射
每個線程都有自己專屬的棧(stack)捂寿,先進后出,棧的最大尺寸固定孵运,超出則引起棧溢出秦陋。變量離開作用范圍后,棧上的數(shù)據(jù)會自動釋放治笨。
堆上內(nèi)存必須手工釋放(C/C++),除非語言執(zhí)行環(huán)境支持GC驳概。
使用棧還是堆?要明確知道數(shù)據(jù)占用多少內(nèi)存旷赖,需要大的內(nèi)存就用堆顺又,反之用棧,若是不確定需要多少內(nèi)存等孵,那就用堆稚照,因為堆可以靈活的使用內(nèi)存。
棧頂從高地址向低地址方向增長俯萌。
棧里面存儲的是變量果录,函數(shù)參數(shù)(從右到左進棧)。
棧先進后出咐熙。
由于某個函數(shù)的變量是存儲在棧里面的弱恒,過了其作用域,存儲該變量地址的棧就把該地址變量給釋放了棋恼。
內(nèi)存映射:操作系統(tǒng)會將物理內(nèi)存地址轉(zhuǎn)化為虛擬內(nèi)存地址(物理內(nèi)存是不連續(xù)的返弹,通過操作系統(tǒng)的映射機制轉(zhuǎn)化為連續(xù)的虛擬內(nèi)存地址,物理內(nèi)存是通過內(nèi)存頁控制的蘸泻,物理內(nèi)存頁越大琉苇,使用率就越低)
calloc函數(shù)
第一個參數(shù)是所需內(nèi)存單元數(shù)量,第二個參數(shù)是每個內(nèi)存單元的大性檬(單位:字節(jié))并扇,calloc自動將分配的內(nèi)存置0,如:calloc(4,sizeof(int));
memset(i,0,sizeof(i));意思是將i所占的內(nèi)存的內(nèi)容都設(shè)置為0。i處要填地址抡诞。
realloc函數(shù)
重新 分配大小穷蛹。舊的地址不用管,會重新分配地址昼汗。格式:realloc(需要重新分配的地址肴熏,分配的個數(shù))
假如有以下的代碼:
問題是自己定義的函數(shù)mystrcpy可以成功的對str賦值嗎?
答案是不能的顷窒,因為形參指針和實參指針都沒有指向任何內(nèi)存蛙吏,即不指向同一個內(nèi)存空間源哩,當(dāng)函數(shù)內(nèi)部完成了對*src的賦值后,并不會對實參str產(chǎn)生任何影響鸦做。
如何解決這個問題励烦,那么就要讓形參指針指向?qū)崊⒅羔樀牡刂罚簿褪切螀⒁且粋€二級指針泼诱,代碼如下:
有如下的代碼
因為坛掠,我們都知道程序中的自動變量和形參都存儲在棧中,那么程序代碼中的buf是變量治筒,存放的是該字符串的首地址屉栓,當(dāng)函數(shù)getstr借宿的時候,buf就自動被釋放了耸袜,就沒有返回值了友多。這邊主要講的是棧和堆的區(qū)別,棧超過其變量的作用域就會被釋放句灌,那么堆就不會了夷陋,需要我們手動的釋放申請的內(nèi)存。在這里我們可以用堆來解決這個問題胰锌。還有記住骗绕,如果返回的是地址,那么返回類型一定要是指針類型资昧,因為指針指向地址取值酬土。如這里buf就代表該數(shù)組的地址,也就是字符串的地址格带。解決如下:
要記住撤缴,手動釋放申請的堆,函數(shù)free叽唱。
有如下代碼:
memset函數(shù)可以將buf數(shù)組所占的內(nèi)存清空為0屈呕,但是不能將指針?biāo)杆峙涞降?00字節(jié)的內(nèi)存清空為0,這是因為sizeof(s)=4,s是指針變量棺亭,存放的是地址虎眨,地址用8位十六進制數(shù)表示,也就是四個字節(jié)镶摘。解決方法就是malloc分配多大嗽桩,我們就清多大,sizeof(char)*100凄敢。
有如下的代碼:
如果將數(shù)組名當(dāng)為函數(shù)的參數(shù)碌冶,那么C語言認(rèn)為數(shù)組名就是指針,那么sizeof(array)就會等于4個字節(jié)涝缝。如果參數(shù)是數(shù)組扑庞,那么一定要有另一個參數(shù)來表示該數(shù)組有多大譬重。
有如下代碼:
代碼解讀:char *s[10];表示的是定義了一個指針數(shù)組嫩挤,首先它是一個數(shù)組害幅,但該數(shù)組的元素是指針,這樣就叫指針數(shù)組岂昭。所以sizeof(s)就等于40。
有如下代碼:
代碼解讀:定義了char(*s1)[10]狠怨;表示定義了一個數(shù)組指針约啊,指向數(shù)組的指針叫做數(shù)組指針。那么s1是個指針變量佣赖,存放的是地址恰矩,所以sizeof(s1)就等于4。
有如下代碼:
代碼解讀:指針s指向了字符串常量憎蛤,且常量和靜態(tài)變量一樣外傅,都存放在靜態(tài)區(qū),程序運行期間俩檬,地址一直有效萎胰,所以指針變量s所指的空間一直有效。不像棧會自動釋放內(nèi)存棚辽。