### 内存中的数据
**程序归根到底还是数据,当执行程序时,数据加载到内存,程序结束后,数据从内存中释放。数据在内存中的存放共分为以下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 命令对音视频编辑处理(已开源)