原来如此简单 ---- 动态内存管理

一、动态内存的四个函数

四个函数的头文件相同 ------> <stdlib.h>

(1)malloc

作用:申请空间

原型:void* malloc (size_t size);

size_t size 指的是申请多大内存空间,单位是字节。传0是未定义行为,会由编译器自己决定。

void* 会有两种返回值。                                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​                                                                                           开辟成功:返回开辟成功空间的的首地址                                                                                            开辟失败:返回NULL

用法如下

int* p = (int*)malloc(10*sizeof(int))
if(p == NULL)
{
  perror("malloc")
  return 1//失败返回非零值
}

开辟完的空间返回的地址类型是不确定的,所以使用void* 指针接收。因为返回的是地址所以用指针接收管理,但是在使用前用强转定义一下类型,否则赋值不上。

有可能会失败,返回NULL,但是你当他成功使用,就会造成对空指针的解引用,所以判断一下。

(2)free

作用:释放空间

原型:void free (void* ptr);

ptr是指给的是动态空间的首元素地址

用法如下:

int* p = (int*)malloc(10*sizeof(int))
if(p == NULL)
{
  perror("malloc")
  return 1//失败返回非零值
}
free(p)
p = NULL;

注意:1.不要释放非动态空间(那种行为是未定义的)                                                                                 2.给的是动态空间的首元素地址,如给给的不是首元素的地址则会导致有部分空间没有释                  放 ,是一种错误行为。                                                                                                                   3.给NULL,free不会做出任何行为。                                                                                                 4.释放完会有“疯狗”出来,及时使用NULL拴住。

申请空间和释放空间函数通常是成对使用的。

(3)calloc

作用:申请空间

原型:void* calloc (size_t num, size_t size);

两个参数的解释:生成num个空间,每个空间的大小是size。

返回开辟空间的地址,失败返回NULL

用法如下:

int* p = (int*)calloc(10,4)
if(p == NULL)
{
  perror("calloc")
  return 1//失败返回非零值
}
free(p)
p = NULL;

和malloc用发类型,注意的是,参数

还有就是他会把申请的空间初始化成0;剩下的点和malloc一样。

(4)realloc

作用:调整空间和申请空间

原型:void* realloc (void* ptr, size_t size);

ptr ---- 被调整空间的首地址,size ----  要调整之后的大小(单位字节)

返回调整空间的首地址(两种调整方式)                                                                                            1.如果申请的动态空间后面还有空间并且刚好够我申请的,那就回直接在之前的空间后面紧挨着申请,返回的是旧空间的地址                                                                                                               2.如果后面空间不够就会重新申请一块空间然后将原来的空间的内容拷贝在新的空间中返回的是这块新空间的首地址

当我们给ptr传的内容是NULL那我们这个函数的作用就和malloc的作用是一样的勒。

当然使用realloc是要用一个新的指针接受,方便操作。

二、常见错误

1.解引用NULL

申请空间失败会返回NULL,如果诸君解引用,那就是非法访问,会报错。例如申请的空间过大,并不够申请的。就会返回NULL。

所以要用动态空间前要判断丫。

2.对动态空间越界访问

int* p = (int*)malloc(40)
int i = 0;
for( i = 0; i <= 10; i++)
{
    *(p + i) = i;
}
free(p);
p = NULL;

申请了10个int类型的空间,结构访问了11个,第11个空间是没有申请的空间,但是却访问并修改,造成了越界访问。

3.对非动态空间使用free(使用free时介绍过)

4.释放部分动态空间(使用free介绍过)

5.释放完之后不用NULL

这样会让使野指针出现。也就是释放完的空间被指针指向,这是非常危险的,攻击者会使用这个野指针来指向我们的内容,造成不可挽回的伤害。

6.内存泄露

也就是占着空间不归还。

就像我们这篇第一个代码只使用malloc但是没有释放,这会造成内存泄漏,如此往复,内存越来越少,可用空间越来越少。这能等程序结束来自动回收空间了,所以要记得回收空间。

三、习题见真章

(1)习题一

它的问题是什么?

(2)习题二

char *GetMemory(void)
{
    char p[] = "hello world";
    return p;
}

 void Test(void)
{
    char *str = NULL;
    str = GetMemory();
    printf(str);
}

那他呢?

四、柔性数组

依赖于结构体,并且包含柔性数组的结构体必须至少有一个成员。

使用sizeof来计算这种结构体时,是不会计算柔性数组的大小的

柔性数组的实现是和malloc 联动来实现的

如下

五、内存的申请

1. 栈区(stack):在执⾏函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时

这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中,效率很⾼,但是分配的内

存容量有限。 栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。

2. 堆区(heap):⼀般由程序员分配释放, 若程序员不释放,程序结束时可能由OS(操作系统)

回收 。分配⽅式类似于链表。

3. 数据段(静态区):(static)存放全局变量、静态数据。程序结束后由系统释放。

4. 代码段:存放函数体(类成员函数和全局函数)的⼆进制代码。

六、习题讲解

1.第一个错误就是解引用空指针

2.返回栈空间地址,你在函数中申请的内存,返回一个指向这个数组的指针,但是你这个函数出来了不就,释放了吗,所以你指向的是一块未经申请的空间你这个指针就是一个野指针。

最后诸君共勉!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值