1.c语言动态内存管理
1.1为什么存在动态内存管理
常见的开辟内存方式有:
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
上诉的开辟空间的方式有两个特点:
1.空间开辟大小是固定的。
2.数组在声明的时候,必须指定数组长度,它所需要的内存在编译时分配
1.2动态内存函数的介绍
malloc 作用:申请空间(连续)
calloc 作用:申请空间+数据初始化
realloc 作用:修改空间大小
free 作用:释放空间
示例:
#include <stdio.h>
#include <stdlib.h>
/*
malloc 作用:申请空间(连续) 效率高 *掌握
calloc 作用:申请空间 + 数据初始化 了解
realloc 作用:修改空间大小
free 作用:释放空间
*/
int main()
{
//1.利用malloc函数申请一片连续的空间
//需求:申请一片空间,要存储100个int类型的整数
//返回这边空间的首地址
int *p = malloc(100 * sizeof(int));
//int* p = calloc(100, sizeof(int));
printf("%p\n", p);
// 2.赋值
for (int i = 0; i < 100; i++)
{
//第一种赋值方式
*(p + i) = (i + 1) * 10;
//第二种赋值
//p[i] = (i + 1) * 10;
}
// 4.扩容,20int类型的整型
int *pp = realloc(p, 20 * sizeof(int));
//3.遍历
for (int i = 0; i < 120; i++)
{
printf("%d\n", *(p + i));
}
// 5.释放空间
//如果申请的空间不需要再进行使用了,那么记得一定要释放
free(pp);
return 0;
}
1.3常见的函数细节点
#include <stdio.h>
#include <stdlib.h>
/*
malloc 申请空间(连续) 掌握
free 释放空间
calloc 申请空间+数据初始化 了解
realloc 修改空间大小
1.malloc 创建空间的单位是字节
2.malloc 返回的是void类型的指针,没有步长的概念,也无法获取空间中的数据,需要强转
3.malloc 返回的仅仅是首地址,没有总大小,最好定义一个变量记录总大小
4.malloc 申请的空间过多,会产生虚拟内存
5.malloc 申请的空间不会自动消失,如果不能正确释放,会导致内存泄漏
6.malloc 申请的空间没有初始化值,需要先赋值才能使用
7.free 释放完空间之后,空间中数据叫做脏数据,可能被清空,可能被修改为其他值
8.calloc就是在malloc的基础上多一个初始化的动作
9.realloc 修改之后的空间,地址值有可能发生变化,也有可能不会改变,但是原本的数据不会丢失
10.realloc 修改之后,无需释放原来的空间,函数底层会进行处理
*/
int main5()
{
// void*通用性 可以转换成其他类型也可以
// int* :指针的步长
// p:首地址
int* p = malloc(25 * sizeof(int));//100字节 // 25 int 50 short 100 char
int size = 25;
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main6()
{
// 5.申请内存空间过多,会产生虚拟内存
//虚拟内存:
// 虚拟:假的
// 当申请的空间过多,因为每一个内存空间不会在刚申请的时候立马使用
// 所以c语言并不会立马就在内存中去开辟空间,而是什么时候存数据了,才会真正的分配内存空间
// 目的:为了提高内存的使用效率
// 表示单词申请空间的字节大小(1G)
int number = 1024 * 1024 * 1024;
//利用循环不断第申请空间
// malloc 申请空间(连续) 掌握
//如果申请空间成功,返回这个空间的首地址
//如果申请失败,则返回NULL
int count = 0;
while (1)
{
int* p = malloc(number);
count++;
if (p == NULL)
{
printf("申请空间失败");
break;
}
printf("内存%d申请成功%p\n", count, p);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main7()
{
//7.释放完空间之后,空间的数据叫做脏数据,可能被清空,可能被修改为其他值
//8.calloc就是在malloc的基础上多一个初始化的动作
//1.申请一片连续的空间存储10个int类型的整数
int* p = malloc(10 * sizeof(int));
int size = 10;
//2.给这片空间赋值
for (int i = 0; i < size; i++)
{
*(p + i) = (i + 1) * 10;
}
//3.遍历
printf("遍历空间中的数据为:\n");
for (int i = 0; i < size; i++)
{
printf("%d ", *(p + i));
}
printf("\n");
//4.释放空间
free(p);
//5.释放之后再次遍历
printf("释放之后遍历数据为:\n");
for (int i = 0; i < size; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
/*
malloc 申请空间(连续)
free 释放空间
calloc 申请空间+数据初始化
realloc 修改空间大小(扩容)
9.realloc修改之后的空间,地址值由可能发生变化,也有可能不会改变,但是原本的数据不会丢失
如果内存中已经无法申请空间了,会返回NULL
10.realloc 修改之后,无需释放原来的空间,函数底层会进行处理
*/
//1.申请一片连续的空间存储10个int类型的整数
int* p = malloc(10 * sizeof(int));
int size = 10;
printf("修改之前的内存地址为:%p\n", p);
//2.给这片空间赋值
for (int i = 0; i < size; i++)
{
*(p + i) = (i + 1) * 10;
}
// 3.修改大小
//realloc修改之后,无需释放原来的空间,函数底层会进行处理
//如果内存地址没变,底层在原来空间的后面接着申请的
//如果内存地址变了,申请一个新的大的空间,把原来的数据拷贝到新的空间当中,再把原来的空间给free掉
int* p2 = realloc(p, 20 * sizeof(int));
int size2 = 20;
printf("修改之后的内存地址为:%p\n", p2);
//3.遍历
printf("遍历空间的数据为:\n");
for (int i = 0; i < size2; i++)
{
printf("%d ", *(p2 + i));
}
printf("\n");
free(p2);
return 0;
}
1.4c语言的内存结构
c语言中堆和栈的区别
1.存储位置:
栈:栈是自动分配的内存区域,通常位于进程的高地址处,它用于存储局部变量和函数调用时的参数和返回地址。
堆:堆是动态分配的内存区域,位于进程的低地址位,它通过调用malloc、calloc,realloc和free等函数来分配和释放内存
2.内存分配:
栈:内存分配自动的,由编译器自动管理。栈的内存分配和释放速度较快,但大小有限
堆:内存分配是动态的,需要程序员手动管理。堆的内存分配和释放速度较慢,但大小可以非常大
3.内存大小:
栈:每个线程都有自己的栈空间,大小通常较小,通常在几kb到几mb之间。
堆:所有线程共享堆空间,大小通常较大,受限于操作系统和硬件的限制。
4.内存管理:
栈:内存管理由操作系统自动完成,程序员不需要手动释放
堆:内存管理需要自己手动完成,程序员需要调用free函数来释放内存,否则会造成内存泄漏
5.访问方式:
栈:数据访问是连续的,通常通过局部变量和函数参数访问。
堆:数据访问可以通过指针进行,灵活性更高
存分配是动态的,需要程序员手动管理。堆的内存分配和释放速度较慢,但大小可以非常大
3.内存大小:
栈:每个线程都有自己的栈空间,大小通常较小,通常在几kb到几mb之间。
堆:所有线程共享堆空间,大小通常较大,受限于操作系统和硬件的限制。
4.内存管理:
栈:内存管理由操作系统自动完成,程序员不需要手动释放
堆:内存管理需要自己手动完成,程序员需要调用free函数来释放内存,否则会造成内存泄漏
5.访问方式:
栈:数据访问是连续的,通常通过局部变量和函数参数访问。
堆:数据访问可以通过指针进行,灵活性更高