接续上篇C语言基础及指针④函数指针
在上一篇我们了解C语言中的函数及函数指针 , 使用函数指针 , 模拟了网络请求的回调方式 , 今天我们来学习动态内存分配。
我们在使用java的时候 , 所有的内存都交由JVM做处理 , 我们无法直接控制 , 虽然很少导致内存溢出 , 但是程序占用内存却会越来越大 , 所以我们在使用Android手机的时候 , 刚开始很流畅 , 用着用着就非常卡 , 在打开大文件或是播放gif的时候 , 如果采用java编写处理引擎 , 则会比较卡 , 因为开辟的内存空间无法控制 , GC回收又不是即时的 , 这时候就需要我们使用JNI技术 , 使用C语言进行处理 。接下来 ,我们就来学习C语言中的动态内存分配 。
C语言中内存的大致分配:
内存 | 描述 | 特性 |
---|---|---|
栈区 | 是一个确定的常数(win 1~2M) 不同平台会有不同大小 超出会提示stackoverflow | 自动分配 , 自动释放 |
堆区 | 用于动态内存分配 | 手动分配和释放 , 可占用80%内存 |
全局区或静态区 | 在程序中明确被初始化的全局变量、静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量) | 只初始化一次 |
程序代码区 | 代码区指令根据程序设计流程依次执行,对于顺序指令,则只会执行一次(每个进程),如果反复,则需要使用跳转指令,如果进行递归,则需要借助栈来实现。 | 代码区的指令中包括操作码和要操作的对象(或对象地址引用) |
C语言中动态分配内存是在堆区 , java语言中new
一个对象 , 也会在堆内存中开辟一块空间 , 来存储我们创建的这个对象 。在C语言中 , 我们在堆区开辟一块空间使用的关键字是malloc
, malloc
函数定义:
void* __cdecl malloc(
_In_ _CRT_GUARDOVERFLOW size_t _Size
);
使用如下:
// 动态内存分配 , 使用malloc函数在对内存中开辟连续的内存空间 , 单位是:字节
// 申请一块40M的堆内存
int* p = (int*)malloc(1024 *1024 * 10 * sizeof(int));
下面我们来模拟一下病毒:
/*动态内存分配*/
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
函数定义:
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);
// 动态分配内存 , 内存空间是连续的
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[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]);
}
// 回收申请的动态内存
if (p2 != NULL)
{
free(p2);
p2 = NULL;
}
system("pause");
}
使用malloc
和realloc
配合 , 就可以模拟出我们java中的集合类型,动态改变内存空间大小 。 使用malloc
第一次申请的内存首地址和第二次申请的内存首地址可能相同也可能不同 , 因为申请的内存是连续的 , 所有 , 但第一次申请的空间的后续空间不够用时 , 会重新开辟新的空间 , 并将数据copy到新的空间里面 。
内存分配的几个注意细节:
1.不能多次释放
2.释放完之后 , 给指针置NULL,标志释放完成
3.内存泄漏 (p重新赋值之后 , 再free , 并没有真正释放 , 要在赋值之前释放前一个内存空间)
Android程序员学C系列:
C语言基础及指针①
C语言基础及指针②之指针内存分析
C语言基础及指针③函数与二级指针
C语言基础及指针④函数指针
C语言基础及指针⑤动态内存分配
C语言基础及指针⑥字符操作
C语言基础及指针⑦结构体与指针
C语言基础及指针⑧文件IO
C语言基础及指针⑨联合体与枚举
C语言基础及指针⑩预编译及jni.h分析