### 內存中的數據
**程序歸根到底還是數據,當執行程序時,數據加載到內存,程序結束后,數據從內存中釋放。數據在內存中的存放共分為以下6種形式**
- 程序代碼區---存放程序編譯后的可執行代碼(二進制代碼)
- 全局區(靜態區)---全局變量和靜態變量的存儲室放在一塊的,初始化的全局變量和初始化的靜態變量在同一區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域,程序結束后由系統釋放
- 常量區---常量就是存放在這里的,程序結束后由系統釋放
- 堆區---一般由程序員分配和釋放,如果程序員不釋放,則出現結束時可能由操作系統回收(程序不正常結束則無法回收)
- 棧區---由編譯器自動分配且釋放,該區域一般存放函數的參數、局部變量
- 寄存器---內存階層的最頂端,一個有限容量的高速存放區,直接建立在中央處理器內部,用來暫存指令,數據和地址,一般用來保存棧頂指針、指令指針和現在正在被運行的指令。由于寄存器區其實是在中央處理器內部建立,而不是內存中,因此嚴格上講,不能將其劃分到內存中,只是它與內存的功能大致相同,都用來暫存數據,所以將其劃分到這里。
### 堆棧概念 靜態內存分配 動態內存分配
* 棧內存
* 系統自動分配
* 系統自動銷毀
* 連續的內存區域
* 向低地址擴展
* 大小固定(如果申請的內存空間超過棧的剩余空間,系統會提示棧溢出,因此不要指望棧能存儲比較多的數據)
* 棧上分配的內存稱為靜態內存
* 棧中的數據執行速度很快
> **棧頂的地址和棧的最大容量是由系統預先設定好的,因此棧的大小是固定的**
* 靜態內存分配
* 子函數執行完,子函數中的所有局部變量都會被銷毀,內存釋放,但內存地址不可能被銷毀,只是地址上的值沒了
* 堆內存
* 程序員手動分配
* java:new
* c:malloc
* 空間不連續(各塊區域由鏈表將它們串聯起來)
* 大小取決于系統的虛擬內存
* C:程序員手動回收free
* java:自動回收
* 堆上分配的內存稱為動態內存
* 堆的上限是由系統中的有效的虛擬內存來定的,因此獲取的空間較大,且獲得空間的方式也比較靈活
* 堆需由程序員自己申請,申請時指明空間大小,同時堆的內存不連續,易產生碎片,數據執行速度慢
> **棧和堆各有優點,好多時候可以結合使用,比如存儲較大數據時,可以將數據放到堆中,而將指向該數據的指針放到棧中,這樣可以有效地提高程序的執行效率;一般來說,只要不是特大的數據,還是建議使用棧,如函數參數,返回值,局部變量,都存放到棧中,這樣可以大大提高程序的運行速度**
**想要數據既不容易被修改,又不會一直占據內存,同時還不局限在某一塊特定區域使用,這時可以使用堆**
**堆的使用比較靈活,可隨時申請一個堆也可以釋放掉一個堆,因此堆中的數據不會一直占據著內存,堆既可以在全局申請,也可以在局部申請,因此堆中數據不會只局限在某一塊特定區域,為了保證數據的隱秘性,堆中的每個內存都是匿名的,不用指針,根本無法找到它,因此堆中數據不容易被修改。**
**靜態內存分配**
```
#include<stdio.h>
#include<stdlib.h>
/**
棧內存 系統統一分配統一回收
靜態內存分配 棧內存大小固定的 內存地址是連續的
main()函數和getData()以及getData2()沒有手動地申請內存,那么所占用的都是棧內存,
*/
int* getData(){
int array[] ={1,2,3,4,5};
printf("getData()中array地址是%#x\n",&array);
return &array;
}
int* getData2(){
int array[] ={5,4,3,2,1};
printf("getData2()中array地址是%#x\n",&array);
return &array;
}
main(){
int* pointer = getData();//getData()函數執行完,內存會被馬上回收,執行getData2()時實際上還是用的getData()的內存
getData2();//通過控制臺輸出結果可知,pointer指的是同一塊內存
printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));//棧內存,系統統一分配統一回收,只能用一次,
printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));//再次打印時,內存被回收或者部分被回收,所以打印出來的值就是隨機的
printf("pointer值為%#x\n",pointer);
system("pause");
}
```
輸出結果
```
getData()中array地址是0x60febc
getData2()中array地址是0x60febc
5,4,3
6,6356652,3
pointer值為0x60febc
```
**動態內存分配**
```
#include<stdio.h>
#include<stdlib.h>
/**
java new對象就會申請一塊堆內存
c malloc memory allocation 內存分配
c的堆內存 程序員手動申請手動釋放 malloc
free
申請一塊堆內存 動態內存分配
堆內存 不連續的
堆內存大小不固定 取決機器的狀態
*/
main(){
//malloc 接收的參數 申請內存大小 返回一個內存地址值 申請到的也是一塊連續的內存空間
int* pointer = malloc(sizeof(int)*5);
*(pointer+0) = 1;
*(pointer+1) = 2;
*(pointer+2) = 3;
*(pointer+3) = 4;
*(pointer+4) = 5;
//C for 循環 循環的臨時變量i 要先聲明再使用
int i;
for(i = 0;i<5;i++){
printf("第%d個元素的值= %d\n",i,*(pointer+i));
}
free(pointer);
printf("第一個元素的值%d\n",*(pointer+0));
system("pause");
}
```
輸出結果
~~~
第0個元素的值= 1
第1個元素的值= 2
第2個元素的值= 3
第3個元素的值= 4
第4個元素的值= 5
第一個元素的值0
~~~
> malloc函數在stdlib.h中聲明,該函數的作用是想系統申請指定字節的內存空間,假如給malloc函數傳遞4,則表示申請4個字節的內存空間,之所以用sizeof(int)代替4,是因為int在不同的編譯環境下所占用的字節數是不一樣的,malloc函數的返回值類型是void*,表示未確定類型的指針
**申請內存空間失敗,對malloc函數返回值進行判斷**
```
#include <stdio.h>
#include <stdlib.h> //包含malloc函數的聲明
#include <LIMITS.h>
int main()
{
int *p=(int*)malloc(2147483648);
if (p==NULL)
printf("申請內存空間失敗");
else
{
*p=4;
printf("%d",*p);
}
return 0;
}
```
> **由于計算機內存有限,可能會出現因內存不足而無法滿足malloc函數請求的情況,這種情況下,malloc函數會返回null,該值被賦給指針后,則該指針就是一個空指針,空指針不會指向有效數據,因此在調用malloc函數申請內存空間時,一定要進行返回值的判斷,而且調用malloc函數申請的空間是不連續的,因此可看做是堆**
**釋放指針指向的堆空間**
```
#include <stdio.h>
#include <stdlib.h> //包含malloc函數和free函數的聲明
int main()
{
int *p=(int*)malloc(sizeof(int));
*p=400;
printf("%d\n",*p);
free(p);
printf("%d\n",*p);
p=0;//當使用free函數釋放p所指向的內存空間后,最好將該指針所保存的內存地址清零,以免重復操作該地址,導致出錯
p=(int*)malloc(sizeof(int));
*p=6;
printf("%d\n",*p);
free(p);//再次將內存回收
return 0;
}
```
輸出結果
~~~
400
0
6
~~~
> **malloc函數申請的內存空間不會被系統自動釋放,因此假如用戶不去釋放它,則該區域的內存將始終不能為其它數據所使用,為了釋放malloc函數申請的內存空間,必需使用free函數**
**使用free函數的注意事項**
- free函數釋放的是指針所指向的內存空間,由于不能將同一塊內存連續釋放兩次,因此不能調用兩次free函數來釋放同一塊內存
- free函數只會釋放指針指向的內存空間,并不會釋放指針所占用的內存,因此指針還存在,而且它仍然保持著原來的內存空間地址,如果再次使用該指針,會出現意想不到的情況
**內存泄漏**
```
#include <stdio.h>
#include <stdlib.h> //包含malloc函數和free函數的聲明
void create()
{
int *p=(int*)malloc(sizeof(int));//create函數中申請一塊內存空間,并由p來保存該空間的地址,
//指針p是個局部變量,因此當create函數調用完畢,指針p就不存在了,p指向的內存空間這也就再也找不到了,
//造成內存丟失,一直到程序結束,p指向的內存空間方才由系統回收。因此假如使用完一塊內存空間,則請立即使用free函數將其釋放
}
int main()
{
create();
return 0;
}
```
> **并不是只有指針丟失才會造成內存泄漏,指針保存的地址也會造成內存泄漏;指針變量只能保存一個地址,如對它重新賦值,則表示以前的地址被覆蓋,假如該地址的內存空間沒有被釋放,則將無法再次通過指針來訪問它,因為此時的指針保存的是新的內存空間地址**
**迷途指針**
```
#include <stdio.h>
#include <stdlib.h> //包含malloc函數和free函數的聲明
int main()
{
int *p=(int*)malloc(sizeof(int));
long *p1;
*p=1;
printf("將1賦給p指向的空間后,指針p讀取到的值:\t\t%d\n",*p);
free(p);//刪除p指向的空間,這樣p就成了一個迷途指針,因為它指向的空間已經不存在了,但是p仍然保存著原來的內存空間的地址,
//這時如果再次嘗試使用該指針,則將會出現意料不到的情況,為了避免出現這種情況,可以在free(p)之后將指針p賦值為0,
//盡管說使空指針是非法的,容易使程序崩潰,但是寧愿程序崩潰也不愿調試起來困難
//p=0;//由于空指針指向的通常不是存在的內存空間,因此導致程序運行過后立即崩潰,從而使我們迅速察覺到是指針出了問題
printf("釋放內存后,指針p讀取到的值:\t\t\t%d\n",*p);//再次打印p的值發現成了隨機產生的一個數而不是1
p1=(long*)malloc(sizeof(long));
printf("申請新內存塊后,指針p保存的地址:\t\t%p\n",p);
*p1=0;
printf("指向新內存塊的指針p1保存的地址:\t\t%p\n",p1);
*p=2;
printf("將2賦給p指向的空間后,指針p讀取到的值:\t\t%d\n",*p);
printf("將2賦給p指向的空間后,指針p1讀取到的值:\t%ld\n",*p1);
free(p1);
return 0;
}
```
#### 指令指針
**指令指針是在寄存器中存放的一種指針,它用來存放下一條待執行指令的地址。每條指令都有一個地址**
- 前言
- JNI基礎知識
- C語言知識點總結
- ①基本語法
- ②數據類型
- 枚舉類型
- 自定義類型(類型定義)
- ③格式化輸入輸出
- printf函數
- scanf函數
- 編程規范
- ④變量和常量
- 局部變量和外部變量
- ⑤類型轉換
- ⑥運算符
- ⑦結構語句
- 1、分支結構(選擇語句)
- 2、循環結構
- 退出循環
- break語句
- continue語句
- goto語句
- ⑧函數
- 函數的定義和調用
- 參數
- 函數的返回值
- 遞歸函數
- 零起點學通C語言摘要
- 內部函數和外部函數
- 變量存儲類別
- ⑨數組
- 指針
- 結構體
- 聯合體(共用體)
- 預處理器
- 預處理器的工作原理
- 預處理指令
- 宏定義
- 簡單的宏
- 帶參數的宏
- 預定義宏
- 文件包含
- 條件編譯
- 內存中的數據
- C語言讀文件和寫文件
- JNI知識點總結
- 前情回顧
- JNI規范
- jni開發
- jni開發中常見的錯誤
- JNI實戰演練
- C++(CPP)在Android開發中的應用
- 掘金網友總結的音視頻開發知識
- 音視頻學習一、C 語言入門
- 1.程序結構
- 2. 基本語法
- 3. 數據類型
- 4. 變量
- 5. 常量
- 6. 存儲類型關鍵字
- 7. 運算符
- 8. 判斷
- 9. 循環
- 10. 函數
- 11. 作用域規則
- 12. 數組
- 13. 枚舉
- 14. 指針
- 15. 函數指針與回調函數
- 16. 字符串
- 17. 結構體
- 18. 共用體
- 19. typedef
- 20. 輸入 & 輸出
- 21.文件讀寫
- 22. 預處理器
- 23.頭文件
- 24. 強制類型轉換
- 25. 錯誤處理
- 26. 遞歸
- 27. 可變參數
- 28. 內存管理
- 29. 命令行參數
- 總結
- 音視頻學習二 、C++ 語言入門
- 1. 基本語法
- 2. C++ 關鍵字
- 3. 數據類型
- 4. 變量類型
- 5. 變量作用域
- 6. 常量
- 7. 修飾符類型
- 8. 存儲類
- 9. 運算符
- 10. 循環
- 11. 判斷
- 12. 函數
- 13. 數學運算
- 14. 數組
- 15. 字符串
- 16. 指針
- 17. 引用
- 18. 日期 & 時間
- 19. 輸入輸出
- 20. 數據結構
- 21. 類 & 對象
- 22. 繼承
- 23. 重載運算符和重載函數
- 24. 多態
- 25. 數據封裝
- 26. 接口(抽象類)
- 27. 文件和流
- 28. 異常處理
- 29. 動態內存
- 30. 命名空間
- 31. 預處理器
- 32. 多線程
- 總結
- 音視頻學習 (三) JNI 從入門到掌握
- 音視頻學習 (四) 交叉編譯動態庫、靜態庫的入門學習
- 音視頻學習 (五) Shell 腳本入門
- 音視頻學習 (六) 一鍵編譯 32/64 位 FFmpeg 4.2.2
- 音視頻學習 (七) 掌握音頻基礎知識并使用 AudioTrack、OpenSL ES 渲染 PCM 數據
- 音視頻學習 (八) 掌握視頻基礎知識并使用 OpenGL ES 2.0 渲染 YUV 數據
- 音視頻學習 (九) 從 0 ~ 1 開發一款 Android 端播放器(支持多協議網絡拉流/本地文件)
- 音視頻學習 (十) 基于 Nginx 搭建(rtmp、http)直播服務器
- 音視頻學習 (十一) Android 端實現 rtmp 推流
- 音視頻學習 (十二) 基于 FFmpeg + OpenSLES 實現音頻萬能播放器
- 音視頻學習 (十三) Android 中通過 FFmpeg 命令對音視頻編輯處理(已開源)