目录
一.内存空间
内存空间分为四部分:栈区,堆区,静态区,内核区
栈区:存储局部变量,函数的形式参数
堆区:存储动态内存分配的数据
静态区:存储全局变量,静态变量
内核区:由操作系统管理
二.动态内存开辟
C语言的动态内存开辟所需要的函数为malloc,realloc,calloc,free
所引用的头文件为stdlib.h
1.malloc函数
函数定义:void *__cdecl malloc(size_t _Size)
表示在堆区开辟一块可用的连续的内存来存储数据,所以需要我们说明开辟的空间的大小,即malloc函数的参数size_t(表示无符号整形)
因为其返回类型为一个void*类型的指针,所以当我们用一个int或其他类型的指针来接收指向这片空间时,就需要对malloc的返回指针进行显式类型转换,即强转
eg:int* p=(int*)malloc(sizeof(int)*30)
由于需要知道具体的开辟空间大小,所以需要通过sizeof计算开辟的数据类型再*开辟个数
malloc开辟空间成功返回该空间的元素首地址,开辟失败则返回一个空指针
2.calloc函数
函数定义:void *__cdecl calloc(size_t _Count, size_t _Size)
第一个参数size_t_Count为开辟数组的元素个数
第二个参数size_t_Size为数组元素占用的字节数
eg:int* p=(int*)calloc(30,sizeof(int))
calloc和malloc函数基本一样,开辟空间的要求也只是格式不同,返回的类型也是一致
差别就是calloc开辟内存空间时会将每个字节初始化为0
3.realloc函数
函数定义: void *__cdecl realloc(void *_Block, size_t _Size)
realloc是在已经利用malloc或calloc开辟的空间后进行调整,可拓展可缩减(缩减会造成数据丢失),若开辟的空间后面没有足够的空间继续开辟,则重新寻找一块新的空间进行新空间的开辟
所以,
第一个参数是已经开辟的空间的指针
第二个参数为新开辟空间的字节数,不是增加的字节数
eg: int* q=(int*)realloc(p,sizeof(int)*50)
表示将空间修改为50内存,并返回指向该空间第一个元素的地址,失败则返回空指针
realloc若开辟的空间不够则会将旧空间的数据复制到新空间并进行开辟
成功则会自动释放旧空间,不需要手动释放
若开辟失败则放回空指针,所以realloc维护的指针和旧空间维护的指针不能是同一个,否则一旦开辟失败该指针置为空那么旧空间的地址也将丢失
所以需要分能否开辟成功来进行讨论
int main()
{
int* p = malloc(sizeof(int) * 30);
int* q = realloc(p, sizeof(int) * 50);
if (q != NULL)//开辟成功
{
p=q;//自动释放旧空间,将新指针赋给旧指针,使指针指向新空间
free(p);
p = NULL;
q = NULL;
}
else//开辟失败,q是空指针,相当于什么都没发生
{
free(p);
p = NULL;
}
system("pause");
return 0;
}
4.free函数
函数定义:void __cdecl free(void *_Block)
参数为要维护动态内存开辟的空间的指针,无返回值
如果不free开辟的空间,程序结束时操作系统也会回收空间,但并不及时,不释放内存空间就会造成内存的泄漏
free后指针指向的空间被释放,但指向动态内存空间的指针仍旧指向该空间,若不将指针置为空,则可能会存在别人利用该指针访问该空间的危险存在,所以free后指针必须置为空
在函数内部开辟空间后最好在函数内部释放,若指针是一个局部变量,存储在栈区中,那么指针出了函数作用域就会被销毁,所以释放不到该内存,但将指针设为静态变量(static)就可以,因为开辟的空间在堆区,不会随着函数的作用域而销毁
5.动态开辟内存易错点
(1)对空指针进行操作
这是最容易犯错的操作,当我们进行动态内存开辟后,往往会默认为开辟成功,一旦开辟失败,动态内存会返回空指针,那么我们就是在对空指针进行操作,无论是对地址的访问还是对空指针的操作都是非法操作
为了避免这种错误,要有对维护开辟的内存的指针进行选择判断的操作,即判断指针是否为空,只有不为空,即开辟成功才能进行操作
(2)对动态开辟的内存的越界访问
类似数组,即访问到了动态内存后面的空间
(3)对非动态开辟内存的指针进行free
free只对堆区空间释放,释放非堆区空间会报错
(4)使用free释放动态开辟内存的一部分
若改变指针的位置,则释放的是改变后的指针指向的后面的空间,前面的空间将丢失,造成错误
所以不能对维护空间的指针进行移动操作,若要操作只能赋值给一个副本进行移动,释放时仍旧释放原指针