標籤:
Android For JNI(四)——C的數組,指標長度,堆記憶體和棧記憶體,malloc,學生管理系統
好幾天每寫JNI了,現在任務也越來越重了,工作的強度有點高,還有好幾個系列的部落格要等著更新,幾本書還嗷嗷待哺的等著我去看,github上的兩個散漫的開源,基礎入門的視頻也在錄製,還要學習新的知識,
都是一種挑戰,不知道為何,最近懶散了,看來還得再加把勁,今天我們繼續延伸一下C的一些小知識
一.數組
C的數組和JAVA也是類似的,我們寫一段小程式
#include <stdio.h> #include <stdlib.h> main(){ char c [] ="lgl"; //列印 每個元素的地址 printf("%#x\n",&c[0]); printf("%#x\n",&c[1]); a printf("%#x\n",&c[2]); printf("%#x\n",&c[3]); //讓視窗停留 int age ; scanf("%d",&age); }
我們列印一下這個數組的每個元素的記憶體位址,會驚奇的發現記憶體位址是連續的
我們繼續理解
//列印C的地址 printf("%#x\n",&c);
我們列印C的地址
得到的結論是第一個記憶體位址,由此我們歸納
- 數組的記憶體空間是連續的
- 陣列變數儲存的是第0個元素的地址
//字元數組的地址,就是一個字元的地址 char* p = &c; printf("第0個元素的值%c\n",*(c + 0)); printf("第0個元素的值%c\n",*(c + 1)); printf("第0個元素的值%c\n",*(c + 2));
這裡,我們就可以拿到lgl這是哪個數了
當然,如果是int類型的話,就需要位移四個單位了
二.指標長度
指標我們可以存地址,我們要想知道他的長度有多長,我們就得去測試一下
#include <stdio.h> #include <stdlib.h> main(){ printf("int*的長度是%d\n",sizeof(int*)); printf("double*的長度是%d\n",sizeof(double*)); //讓視窗停留 int age ; scanf("%d",&age);}
輸出的結果
指標長度為4,因為32位的環境,記憶體長度為4,足夠了
三.堆記憶體和棧記憶體
JAVA也有類似的說法,我們定義一個變數,就在記憶體中開闢一個控制項,那具體開闢在哪呢,這就要看我們的類型了,我這裡畫張圖,好理解一下
在JAVA中,我們定義一個變數比如int i是在棧記憶體開闢控制項,persion p 也是,但是我們new了之後,對象是在堆記憶體裡。在C中,我們沒有new,我們有一個申請記憶體的函數malloc,但是java也有其他的因果,比如我們寫個數組,其實k就在棧記憶體,但是數組對象卻在堆記憶體
堆和棧的區別:
1.申請方式
棧:
由系統自動分配.例如,聲明一個局部變數int b; 系統自動在棧中為b開闢空間.例如當在調用涵數時,需要儲存的變數,最明顯的是在遞迴調用時,要系統自動分配一個棧的空間,後進先出的,而後又由系統釋放這個空間.
堆:
需要程式員自己申請,並指明大小,在c中用malloc函數
如char* p1 = (char*) malloc(10); //14byte
但是注意p1本身是在棧中的.
2 申請後系統的響應
棧:只要棧的剩餘空間大於所申請空間,系統將為程式提供記憶體,否則將報異常提示棧溢出。
堆:首先應該知道作業系統有一個記錄空閑記憶體位址的鏈表,當系統收到程式的申請時, 會遍曆該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程式,另外,對於大多數系統,會在這塊記憶體空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本記憶體空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閑鏈表中。
3.申請大小的限制
棧:在Windows下,棧是向低地址擴充的資料結構,是一塊連續的記憶體的地區。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(vc編譯選項中可以設定,其實就是一個STACK參數,預設2M),如果申請的空間超過棧的剩餘空間時,將提示overflow。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴充的資料結構,是不連續的記憶體地區。這是由於系統是用鏈表來儲存的空閑記憶體位址的,自然是不連續的,而鏈表的遍曆方向是由低地址向高地址。堆的大小受限於電腦系統中有效虛擬記憶體。由此可見,堆獲得的空間比較靈活,也比較大。
4.申請效率的比較:
棧:由系統自動分配,速度較快。但程式員是無法控制的。
堆:由malloc/new分配的記憶體,一般速度比較慢,而且容易產生記憶體片段,不過用起來最方便.
5.堆和棧中的儲存內容
棧:在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,然後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然後是函數中的局部變數。注意靜態變數是不入棧的。
當本次函數調用結束後,局部變數先出棧,然後是參數,最後棧頂指標指向最開始存的地址,也就是主函數中的下一條指令,程式由該點繼續運行。
堆:一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體內容有程式員安排。
6.記憶體的回收
棧上分配的記憶體,編譯器會自動收回;堆上分配的記憶體,要通過free來顯式地收回,否則會造成記憶體流失。
堆和棧的區別可以用如下的比喻來看出: \
使用棧就像我們去飯館裡吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
使用堆就像是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。
四.malloc
既然瞭解了,那我們就來使用malloc申請堆記憶體
#include <stdio.h> #include <stdlib.h> main(){ //比如存三個int int是4位的 所以寫12 這12個位元組的記憶體位址是連續的, //他返回 的是堆記憶體的首地址 int* p = malloc(3 * sizeof(int)); //賦值 *(p + 1) = 3; //列印值 printf("%d\n",*(p+1)); // = 3? //列印地址 printf("%#x\n",(p+1)); //讓視窗停留 int age ; scanf("%d",&age); }
我們輸出結果
申請之後怎麼釋放呢?
//釋放堆記憶體 free(p);
我們可以嘗試一下釋放
#include <stdio.h> #include <stdlib.h> main(){ //比如存三個int int是4位的 所以寫12 這12個位元組的記憶體位址是連續的, //他返回 的是堆記憶體的首地址 int* p = malloc(3 * sizeof(int)); //賦值 *(p + 1) = 3; //列印值 printf("%d\n",*(p+1)); // = 3? //列印地址 printf("%#x\n",(p+1)); //釋放堆記憶體 free(p); //列印值 printf("%d\n",*(p+1)); // = 3? //列印地址 printf("%#x\n",(p+1)); //讓視窗停留 int age ; scanf("%d",&age); }
我們會發現,記憶體釋放了之後,值就變了,當然記憶體不可能被釋放,而且P一直指向的是棧記憶體
五.後台管理系統
我們這裡寫個小例子,說起來這麼高大上,其實就是一段小程式,學號管理id小程式,我們玩玩
程式本身還是很簡單的,就是把前面幾篇的小知識柔和在了一起
#include <stdio.h> #include <stdlib.h> main(){ printf("請輸入班級人數:"); int count ; scanf("%d",&count); //有多少個人就有多少個學號 int i ; //申請堆記憶體 儲存學號 int* p = malloc(count * sizeof(int)); for(i = 1; i <= count; i++){ printf("請輸入第%d個學生的學號:",i); scanf("%d",(p + i - 1)); } // 列印所有的學號 for(i = 1; i <= count; i++){ printf("第%d個學生的學號是%d\n",i,*(p+i-1)); } int age ; scanf("%d",&age); system("pause");}
運行一下就知道效果了
當然,我們既然是管理系統,插入嘛,我們繼續往下寫
我們這裡會接觸到一個新的API,我們看下去
#include <stdio.h> #include <stdlib.h> main(){ printf("請輸入班級人數:"); int count ; scanf("%d",&count); //有多少個人就有多少個學號 int i ; //申請堆記憶體 儲存學號 int* p = malloc(count * sizeof(int)); for(i = 1; i <= count; i++){ printf("請輸入第%d個學生的學號:",i); scanf("%d",(p + i - 1)); } //插入 printf("請輸入要插入的人數:"); int newCount; scanf("%d",&newCount); //重新申請堆記憶體 //在原有的記憶體中擴充新的空間 //當然,前提要確定可不可以擴充,如果記憶體被暫用,系統會自動分配記憶體 //新記憶體會把舊記憶體拷貝過去,並且釋放舊的記憶體 p = realloc(p,(count + newCount) * sizeof(int)); for(i = count+1; i <= count+newCount; i++){ printf("請輸入第%d個學生的學號:",i); scanf("%d",(p + i - 1)); } // 列印所有的學號 for(i = 1; i <= count + newCount; i++){ printf("第%d個學生的學號是%d\n",i,*(p+i-1)); } int age ; scanf("%d",&age); system("pause");}
這裡注意的就是realloc了,注釋也很明了的解析了,我們運行一下
OK,暫時學到這裡!!
Android For JNI(四)——C的數組,指標長度,堆記憶體和棧記憶體,malloc,學生管理系統