存在动态内存分配的意义:
int main()
{
int i = 10;//在栈空间上开辟四个字节的空间
char arr[10] = { 0 };//在栈空间上开辟10个字节的连续空间
return 0;
}
/*
上述开辟空间的方式存在连个特点:
1、空间开辟的大小是固定的
2、数组在申明的时候,必须给定确定的值,它所需要的内存在编译时分配
但对于空间的需求,不仅仅有上述情况。有时候我们需要的空间大小在程序运行时才能知道,如果人为随便给定一个大小,则会造成空间的浪费,
那么数组在编译时开辟空间的方式就不适合了。这时候就需要用动态内存开辟空间了(堆区)
*/
#include<stdlib.h>
#include<string.h>
#include<errno.h>
动态内存函数的介绍(还包含free函数)
1、malloc
int main()
{
int* p=(int*)malloc(sizeof(int)*10);
if (p == NULL)//判断p是否为空指针,如果是,则打印原因
printf("%s", strerror(errno));
else
{
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 100; i++)
{
printf("%d ", *(p + i));
}
free(p);//专门用来释放动态开辟的内存函数
p = NULL;//由于在释放完内存后p指针仍然在原来的位置,存在危险访问的情况,因此将p变成空指针。
}
return 0;
}
/*
void *malloc( size_t size );//malloc函数的声明
malloc函数向内存申请一块连续可用的空间,并返回指向这块空间的指针
如果开辟成功,则返回一个指向开辟好空间的指针。
如果开辟失败,则返回空指针。因此,malloc的返回值一定要做检查。
返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体使用时由使用者自己决定。(利用强制类型转换)
如果参数size为0,malloc的行为是标准未定义的,取决于编译器
*/
2、calloc
int main()
{
int* p = (int*)calloc(10 , sizeof(int));
if (p == NULL)
printf("开辟内存失败\n");
else
{
//打印开辟空间中的数值
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p);
p = NULL;
}
return 0;
}
/*
void *calloc( size_t num, size_t size );//calloc函数的声明
函数的功能是将num个大小(字节)为size的元素开辟一块空间,并且把空间的每个字节都初始化成0
*/
calloc函数与malloc函数的区别:
两者的区别仅仅在于calloc会在返回地址之前把申请的空间的每个字节都初始化成0。
3、realloc
int main()
{
//开辟一个5个整形大小的空间
int* p =(int*) malloc(5 * sizeof(int));
if (p == NULL)
printf("开辟内存失败\n");
else
{
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = i;
}
//此时如果觉得空间不够大,需要增大存储空间
int* ptr = (int*)realloc(p, 40);
if (ptr == NULL)
{
printf("追加内存失败\n");
free(p);
p == NULL;
}
else
{
p = ptr;
int j = 0;
for (j = i; j < 10; j++)
{
*(p + j) = j;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p);
p ==NULL;
ptr = NULL;
}
}
return 0;
}
/*
void *realloc( void *memblock, size_t size );//realloc函数的声明
realloc的第一个参数是指向需要增大空间的指针,第二个参数是需要开辟总空间的大小。作用:增大空间
*/
/*
realloc使用的注意事项:
1、如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p
2、如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一个新的内存区域开辟一块满足要求的空间,并且将原来的数据拷贝
到新开辟的空间中,并释放掉旧的空间。最后返回新开辟的内存空间地址
3、若需要追加的空间过大,超过了堆区可用的动态内存,则会返回一个空指针,所以首先应该创建新的指针接收realloc返回值,当判断返回值不是空
指针时,再将该指针赋给p。
*/
常见的动态内存错误(err)(括号中的代码仅便于理解,非标准代码)
1、对NULL指针进行解引用操作(p=NULL;*p=20;)
2、对动态开辟空间的越界访问(int* p=malloc(12);*(p+5)=20;——越界访问)
3、对非动态开辟内存使用free释放(int a=10;int* p=&a;free(p)——err)
4、使用free释放动态开辟内存的一部分
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
printf("开辟空间失败\n");
else
{
int i = 0;
for (i = 0; i < 5; i++)
{
*p++ = i;
}
free(p);//此时p指向的不再是起始位置,只有p指向的是开辟空间起始位置时才能使用free函数释放动态内存开辟的空间。
//解决方案:不改变p的值,使用p+数字或下标的形式来改变指针位置
p = NULL;
}
return 0;
}
5、对同一块动态内存进行多次释放(int* p=malloc(40) ;free(p);free(p);)
解决方案:1、谁开辟谁回收 2、每释放一次内存,将指向该内存的空间设置为空指针
6、对动态开辟的内存忘记释放(内存泄漏)
动态内存管理与静态内存的区别(可能会有不准确的地方):
1、动态内存管理是在堆区开辟空间,静态内存是在栈区
2、开辟动态内存需要使用到指针,而开辟静态内存指针不需要
3、静态内存数据会存在压栈现象,而动态内存是用指针开辟的空间,不存在类似压栈的情况
C/C++程序的内存开辟
/*
C/C++程序内存分配的几个区域:
1、栈区:在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令
集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量,函数参数,返回数据,返回地址等。
2、堆区:一般由程序员分配和释放,若程序员不释放,程序结束时可能由系统回收。分配方式类似于链表。
3、数据段(静态区):存储static修饰的局部变量(静态数据),全局变量。程序结束后由系统释放
4、代码段:存放函数体(类成员函数和全局函数)的二进制代码
*/