动态内存管理上

简介: 动态内存管理上

目录

动态内存分配存在的原因

动态内存函数的介绍

malloc和free

calloc

realloc

常见的动态内存错误
对NULL指针的解引用操作

对动态开辟空间的越界访问

对非动态开辟内存使用free释放

使用free释放一块动态开辟内存的一部分  

对同一块动态内存多次释放

动态开辟内存忘记释放(内存泄漏)
几个经典的笔试题

题一

题目二

题目三

题目四

C/C++程序的内存开辟

柔性数组

柔性数组的特点

柔性数组的使用

柔性数组的优势

总结


动态内存分配存在的原因

我们先看两种内存开辟方式

int val = 20 ; // 在栈空间上开辟四个字节
char arr [ 10 ] = { 0 }; // 在栈空间上开辟 10 个字节的连续空间

但是上述的开辟空间的方式有两个特点:

1. 空间开辟大小是固定的。

2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道, 那数组的编译时开辟空间的方式就不能满足了。 这时候就只能试试动态存开辟了。


动态内存函数的介绍

malloc和free

C语言提供了一个动态内存开辟的函数:

void* malloc ( size_t size );

这个函数向内存申请一块 连续可用 的空间,并返回指向这块空间的指针。

如果开辟成功,则返回一个指向开辟好空间的指针。

如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。

如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

C 语言提供了另外一个函数 free ,专门是用来做动态内存的释放和回收的,函数原型如下:void


free ( void* ptr );

free 函数用来释放动态开辟的内存。


如果参数 ptr 指向的空间不是动态开辟的,那 free 函数的行为是未定义的。

如果参数 ptr 是 NULL 指针,则函数什么事都不做。

malloc和free都声明在 stdlib.h 头文件中。

eg:

#include <stdio.h>
int main ()
{
// 代码 1
        int num = 0 ;
        scanf ( "%d" , & num );
        int arr [ num ] = { 0 };
// 代码 2
        int* ptr = NULL ;
        ptr = ( int* ) malloc ( num * sizeof ( int ));
if ( NULL != ptr ) // 判断 ptr 指针是否为空
{
        int i = 0 ;
        for ( i = 0 ; i < num ; i ++ )
        {
                * ( ptr + i ) = 0 ;
         }
}
        free ( ptr ); // 释放 ptr 所指向的动态内存
        ptr = NULL ; // 是否有必要?
        return 0 ;
}

注意:这里的ptr=NULL是很有必要的,因为当ptr所指向的动态内存被释放后。如果不给ptr进行赋值,ptr就会变成野指针 。


calloc

C语言还提供了一个函数叫 calloc , calloc 函数也用来动态内存分配。原型如下:

void* calloc (size_t num, size_t size);

函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为 0 。

与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全 0 。


eg:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int* p = (int*)calloc(10, sizeof(int));
    if (NULL != p)
    {
        //使用空间
    }
    free(p);
    p = NULL;
    return 0;
}

8870b246f5994226be5c2a9bab382740.png

所以如何我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务。


realloc


realloc 函数的出现让动态内存管理更加灵活。

有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时

候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小

的调整。

函数原型如下:


void* realloc (void* ptr, size_t size);

ptr 是要调整的内存地址

size 调整之后新大小

返回值为调整之后的内存起始位置。

这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。


realloc在调整内存空间的是存在两种情况:


       情况1:原有空间之后有足够大的空间  

       情况2:原有空间之后没有足够大的空间

情况 1

当是情况 1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。

情况 2

当是情况 2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小

的连续空间来使用。这样函数返回的是一个新的内存地址。

由于上述的两种情况, realloc 函数的使用就要注意一些。

eg:


int main()
{
 int *ptr = (int*)malloc(100);
 if(ptr != NULL)
 {
     //业务处理
 }
 else
 {
     exit(EXIT_FAILURE);    
 }
 int*p = NULL;
 p = realloc(ptr, 1000);
 if(p != NULL)
 {
 ptr = p;
 }
 //业务处理
 free(ptr);
 return 0;
}

注意:因为扩展不一定会成功,所以在扩展后,一定要进行判断,才能赋给ptr

常见的动态内存错误

NULL指针的解引用操作

void test()
{
 int *p = (int *)malloc(INT_MAX/4);
 *p = 20;
 free(p);
}


如果p的值是NULL,就会出现问题


对动态开辟空间的越界访问

void test()
{
 int i = 0;
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL == p)
 {
 exit(EXIT_FAILURE);
 }
 for(i=0; i<=10; i++)
 {
 *(p+i) = i;
 }
 free(p);
}

总共增加了10个元素,下标最多遍历到9,所以当i=10的时候越界访问了,程序就崩掉了


09d722d55104498ab76af73c94967f49.png


对非动态开辟内存使用free释放

void test()
{
     int a = 10;
     int *p = &a;
     free(p);
}

这里呢p为非动态开辟内存,不能用free释放,不然程序会崩溃


c46d232eed224e87931adfca9bd4ed67.png

使用free释放一块动态开辟内存的一部分

此时p不再指向动态内存的起始位置 ,如果运行程序就会崩掉


不能只释放一部分


要从起始位置开始


从头持续到结尾




2b0749a105764670b0ea830f2a4fe97a.png



对同一块动态内存多次释放

void test()
{
     int *p = (int *)malloc(100);
     free(p);
     free(p);
}

这里p进行了重复释放,程序会崩掉


8344999e3a7841c7a0afab1100063c0d.png


动态开辟内存忘记释放(内存泄漏)

void test()
{
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
}
int main()
{
 test();
 while(1);
}


注意:malloc申请的动态内存只有两种释放方式:1、程序结束 2、用free进行释放


而这里程序既未结束,也没用free进行释放,忘记释放不再使用的动态开辟的空间会造成内存泄漏。


切记:


动态开辟的空间一定要释放,并且正确释放 。



几个经典的笔试题


题一

void GetMemory(char *p)
{
     p = (char *)malloc(100);
}
void Test(void)
{
     char *str = NULL;
     GetMemory(str);
     strcpy(str, "hello world");
     printf(str);
}


形参是实参的临时拷贝,代码对p进行了操作,但是对str毫无影响



6dc5c0940c98446295be36e086706461.png



而且这里由于GetMemory函数已经结束,已经找不到释放的位置,此处无法释放


对NULL指针进行引用操作,程序会崩溃且会出现内存泄漏


出了GetMemory后就会变成野指针


相关文章
|
19天前
|
编译器
动态内存管理
动态内存管理
18 0
|
7月前
|
编译器
【动态内存管理】
【动态内存管理】
35 0
|
2月前
|
编译器 程序员 C语言
动态内存管理(超详细!)
动态内存管理(超详细!)
27 2
|
6月前
|
C语言 Python
动态内存管理(下)
动态内存管理(下)
29 0
|
2月前
|
程序员 C语言 C++
详解动态内存管理!
详解动态内存管理!
|
5月前
|
编译器
动态内存管理(1)
动态内存管理(1)
36 0
|
5月前
|
程序员 编译器
动态内存管理-1
动态内存管理
33 0
|
6月前
|
C语言
动态内存管理(上)
动态内存管理(上)
23 0
|
6月前
动态内存管理(下)
动态内存管理(下)
18 0
|
8月前
|
编译器 C++
动态内存管理详解
动态内存管理详解