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