接續(xù)上篇C語言基礎及指針④函數(shù)指針
在上一篇我們了解C語言中的函數(shù)及函數(shù)指針 , 使用函數(shù)指針 , 模擬了網(wǎng)絡請求的回調方式 , 今天我們來學習動態(tài)內存分配。
我們在使用java的時候 , 所有的內存都交由JVM做處理 , 我們無法直接控制 , 雖然很少導致內存溢出 , 但是程序占用內存卻會越來越大 , 所以我們在使用Android手機的時候 , 剛開始很流暢 , 用著用著就非???, 在打開大文件或是播放gif的時候 , 如果采用java編寫處理引擎 , 則會比較卡 , 因為開辟的內存空間無法控制 , GC回收又不是即時的 , 這時候就需要我們使用JNI技術 , 使用C語言進行處理 。接下來 ,我們就來學習C語言中的動態(tài)內存分配 。
C語言中內存的大致分配:
內存 | 描述 | 特性 |
---|---|---|
棧區(qū) | 是一個確定的常數(shù)(win 1~2M) 不同平臺會有不同大小 超出會提示stackoverflow | 自動分配 , 自動釋放 |
堆區(qū) | 用于動態(tài)內存分配 | 手動分配和釋放 , 可占用80%內存 |
全局區(qū)或靜態(tài)區(qū) | 在程序中明確被初始化的全局變量、靜態(tài)變量(包括全局靜態(tài)變量和局部靜態(tài)變量)和常量數(shù)據(jù)(如字符串常量) | 只初始化一次 |
程序代碼區(qū) | 代碼區(qū)指令根據(jù)程序設計流程依次執(zhí)行,對于順序指令,則只會執(zhí)行一次(每個進程),如果反復,則需要使用跳轉指令,如果進行遞歸,則需要借助棧來實現(xiàn)。 | 代碼區(qū)的指令中包括操作碼和要操作的對象(或對象地址引用) |
C語言中動態(tài)分配內存是在堆區(qū) , java語言中new
一個對象 , 也會在堆內存中開辟一塊空間 , 來存儲我們創(chuàng)建的這個對象 。在C語言中 , 我們在堆區(qū)開辟一塊空間使用的關鍵字是malloc
, malloc
函數(shù)定義:
void* __cdecl malloc(
_In_ _CRT_GUARDOVERFLOW size_t _Size
);
使用如下:
// 動態(tài)內存分配 , 使用malloc函數(shù)在對內存中開辟連續(xù)的內存空間 , 單位是:字節(jié)
// 申請一塊40M的堆內存
int* p = (int*)malloc(1024 *1024 * 10 * sizeof(int));
下面我們來模擬一下病毒:
/*動態(tài)內存分配*/
void heapFunc() {
// 動態(tài)內存分配 , 使用malloc函數(shù)在對內存中開辟連續(xù)的內存空間 , 單位是:字節(jié)
// 申請一塊40M的堆內存
int* p = (int*)malloc(1024 *1024 * 10 * sizeof(int));
}
void main() {
while (1)
{
// 睡一秒執(zhí)行一次
Sleep(1000);
heapFunc();
}
getchar();
}
打開任務管理器 , 我們可以看到我們共存所占內存 , 正在以40M每秒的速度 , 蹭蹭的往上漲 , 以前的蠕蟲病毒就是如此 , 不斷的消耗內存 , 然后導致系統(tǒng)崩潰 。
在使用靜態(tài)內存分配的時候 , 內存大小是固定的 , 很容易超出棧內存的最大值, 預估大小往往大大的超出使用大小 , 浪費內存 。使用malloc
申請內存 , 最重要的一個點就是可以動態(tài)改變申請的內存大小 , 可以使用realloc
函數(shù)來重新申請內存大小,realloc
函數(shù)定義:
void* __cdecl realloc(
_Pre_maybenull_ _Post_invalid_ void* _Block,
_In_ _CRT_GUARDOVERFLOW size_t _Size
);
使用如下:
// 重新申請內存大小 , 傳入申請的內存指針 , 申請內存總大小
int* p2 = realloc(p, (len + add) * sizeof(int));
下面我們來應用一下:
void main() {
int len;
printf("請輸入首次分配內存大?。?);
scanf("%d", &len);
// 動態(tài)分配內存 , 內存空間是連續(xù)的
int* p = (int*)malloc(len * sizeof(int));
// 給申請的內存空間賦值
int i = 0;
for (; i < len ; i++)
{ // 生成隨機數(shù)賦值
p[i] = rand() % 100;
printf("array[%d] = %d , %#x\n", i, p[i], &p[i]);
}
// 在原有內存上面,重新分配內存大小
printf("請輸入增加的內存大小");
int add;
scanf("%d", &add);
// 重新申請內存大小 , 傳入申請的內存指針 , 申請內存總大小
int* p2 = (int*)realloc(p, (len + add) * sizeof(int));
// 給新申請的內存空間賦值
int j = len;
for (; j < len + add ; j++)
{
p2[j] = rand() % 200;
}
// 打印
j = 0;
for (; j < len + add; j++)
{
printf("array[%d] = %d , %#x\n", j, p2[j], &p2[j]);
}
// 回收申請的動態(tài)內存
if (p2 != NULL)
{
free(p2);
p2 = NULL;
}
system("pause");
}
使用malloc
和realloc
配合 , 就可以模擬出我們java中的集合類型,動態(tài)改變內存空間大小 。 使用malloc
第一次申請的內存首地址和第二次申請的內存首地址可能相同也可能不同 , 因為申請的內存是連續(xù)的 , 所有 , 但第一次申請的空間的后續(xù)空間不夠用時 , 會重新開辟新的空間 , 并將數(shù)據(jù)copy到新的空間里面 。
內存分配的幾個注意細節(jié):
1.不能多次釋放
2.釋放完之后 , 給指針置NULL,標志釋放完成
3.內存泄漏 (p重新賦值之后 , 再free , 并沒有真正釋放 , 要在賦值之前釋放前一個內存空間)
Android程序員學C系列:
C語言基礎及指針①
C語言基礎及指針②之指針內存分析
C語言基礎及指針③函數(shù)與二級指針
C語言基礎及指針④函數(shù)指針
C語言基礎及指針⑤動態(tài)內存分配
C語言基礎及指針⑥字符操作
C語言基礎及指針⑦結構體與指針
C語言基礎及指針⑧文件IO
C語言基礎及指針⑨聯(lián)合體與枚舉
C語言基礎及指針⑩預編譯及jni.h分析