动态内存管理(静态内存和动态内存管理的区别,malloc,calloc,realloc的用法及其区别,C/C++程序内存分配的几个区域)

发布于:2022-12-12 ⋅ 阅读:(381) ⋅ 点赞:(0)

存在动态内存分配的意义:
 

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、代码段:存放函数体(类成员函数和全局函数)的二进制代码
*/

 

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到