服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言 超详细梳理总结动态内存管理

C语言 超详细梳理总结动态内存管理

2022-11-01 15:38二球悬铃木丶 C/C++

动态内存是相对静态内存而言的。所谓动态和静态就是指内存的分配方式。动态内存是指在堆上分配的内存,而静态内存是指在栈上分配的内存,本文带你深入探究C语言中动态内存的管理

一.为什么存在动态内存分配

我们已经掌握的内存开辟方式有:

?
1
2
int a = 10;//在栈空间开辟4个字节的连续空间
int b[20] = { 0 };//在栈空间开辟20个字节的连续空间

这种开辟空间的方式有以下特点:

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

2.开辟数组时必须指定大小

初学数组时,我写过下面的错误代码。

?
1
2
3
int N;
scanf("%d",&N);
int a[N]={ 0 };

可N是变量,不能用于数组元素个数的初始化。

如果我们需要的空间大小在程序运行时才能知道,那就只能试试动态内存开辟了。

二.动态内存函数的介绍

1.malloc和free

void* malloc (size_t size); 
void free (void* ptr);

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

若开辟成功,返回指向这块空间的指针

若开辟失败,返回NULL指针,因此malloc的返回值一定要做检查

使用完malloc函数要用free释放申请的内存空间

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
#include<stdlib.h>
 
int main()
{
    int* p = (int*)malloc(40);//开辟40个字节的栈空间
    if (p == NULL)            //检查是否为空指针
    {
        perror("malloc");
        return 1;
    }
    for (int i = 0; i < 10; i++)
    {
        *(p + i)=i;
    }
    free(p);                 //用完后释放空间,注意参数为首地址
    p = NULL;                //置为空指针
}

2.calloc

void* calloc (size_t num, size_t size)

calloc的两个参数分别为申请元素的个数和每个元素的大小,

使用和malloc差不多,但是申请的空间会被初始化为0,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
#include<stdlib.h>
 
int main()
{
    int* p = (int*)calloc(10, sizeof(int));
    if (p == NULL)
    {
        perror("calloc");
        return 1;
    }
    for (int i = 0; i < 10; i++)
    {
        printf("%d  ", *(p + i)); //输出为 10个0
    }
    free(p);
    p = NULL;
}

3.realloc

void* realloc (void* ptr, size_t size)

realloc用于重新设置要开辟内存空间的大小,可以是增大或减小

指针ptr是指向先前使用 malloc、calloc 或 realloc 分配的内存块的指针。

size 是新开辟内存空间的大小

若原空间后面未开辟的空间足够使用,则返回原先的起始地址

C语言 超详细梳理总结动态内存管理

 若原空间后面未开辟的空间不足以填满新开辟内存空间,

则会在某个地址开辟所需要的空间,free掉原空间的地址,

并且返回新的地址的起始地址

真 ·  一条龙服务

C语言 超详细梳理总结动态内存管理

 若扩容失败,会返回空指针,因此也要检查是否是空指针

三.常见的动态内存错误

1.对NULL指针的解引用操作

?
1
2
3
4
5
6
void test()
{
    int *p = (int*)malloc(INT_MAX/4);
    *p = 20;
    free(p);
}

若p为空指针,则程序错误。

解决方案:检查是否为空指针

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
    int* p = (int*)calloc(10, sizeof(int));
    if (p == NULL)
    {
        perror("calloc");
        return 1;
    }
    for (int i = 0; i <= 10; i++)  //当i为10时,形成越界访问,程序出错
    {
        printf("%d  ", *(p + i));
    }
    free(p);
    p = NULL;
}

使用这块空间时要注意是否已经越界访问

3.对非动态开辟的空间使用free释放

?
1
2
3
int a = 10;
int* p = &a;
free(p);

一执行,程序崩溃了

C语言 超详细梳理总结动态内存管理

4.使用free释放一块动态开辟空间的一部分

?
1
2
3
4
5
6
void test()
{
    int* p = (int*)malloc(100);
    p++;
    free(p);
}

同样会崩溃

C语言 超详细梳理总结动态内存管理

5.对同一块开辟的空间多次释放

?
1
2
3
4
5
6
void test()
{
    int* p = (int*)malloc(100);
    free(p);
    free(p);
}

没错,又又又崩溃了,就不上图了

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

如果使用空间后不释放,会导致内存泄漏。

内存泄漏的堆积,这会最终消耗尽系统所有的内存。

四.几个经典的笔试题

第一个

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void GetMemory(char* p)            //对空指针解引用
{
    p = (char*)malloc(100);        //内存泄露
}
void test()
{
    char* str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");   
    printf(str);
}
 
int main()
{
    test();
 
}

p是str的一份临时拷贝,指向malloc申请的起始地址,

出了函数之后,内存还给系统,str仍为空指针,strcpy把“hello world”放进空指针

第二个

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char *GetMemory(void)
{
    char p[] = "hello world";
    return p;
}
void test()
{
    char* str = NULL;
    str=GetMemory();        //野指针str
    printf(str);
}
 
int main()
{
    test();
 
}

定义字符串p,并返回p的地址

但是当出了这个函数,内存还给系统,没有使用权限

指针变为

第三个

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void GetMemory(char** p,int num)            //传址调用
{
    *p = (char*)malloc(num);       
}
void test()
{
    char* str = NULL;
    GetMemory(&str,100);
    strcpy(str, "hello world");
    printf(str);                               
                                            //没有free
}
 
int main()
{
    test();
 
}

打印hello world

没有释放空间

第四个

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void GetMemory(char** p,int num)
{
    *p = (char*)malloc(num);
}
void test()
{
    char* str = (char*)malloc(100);
    strcpy(str, "hello world");
    free(str);                            //还给你,我还要用,哼~
    if (str != NULL)
    {
        strcpy(str, "!!!");
        printf(str);
    }
}
 
int main()
{
    test();
 
}

开辟100个字节的空间后,又把这块空间还给操作系统。

再次把“!!!”放进这块空间,非法修改

tips:动态内存管理是在堆区上进行的。

到此这篇关于C语言 超详细梳理总结动态内存管理的文章就介绍到这了,更多相关C语言 动态内存管理内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/m0_63742310/article/details/123811337

延伸 · 阅读

精彩推荐
  • C/C++C/C++字节序的深入理解

    C/C++字节序的深入理解

    本文主要介绍了C/C++字节序的深入理解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    英雄哪里出来7292022-09-27
  • C/C++虚函数表-C++多态的实现原理解析

    虚函数表-C++多态的实现原理解析

    这篇文章主要介绍了虚函数表-C++多态的实现原理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...

    sherlock_lin6272021-10-21
  • C/C++C++实现DES加密算法实例解析

    C++实现DES加密算法实例解析

    这篇文章主要介绍了C++实现DES加密算法实例解析,是一个很实用的功能,需要的朋友可以参考下...

    C++教程网12982021-01-27
  • C/C++C语言结构体中内存对齐的问题理解

    C语言结构体中内存对齐的问题理解

    内存对齐”应该是编译器的“管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置上。但是C语言的一个特点就是太灵活,太强大,它允许你...

    诚挚的乔治8512022-09-16
  • C/C++C++设计模式之装饰模式

    C++设计模式之装饰模式

    这篇文章主要介绍了C++设计模式之装饰模式,装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能,需要的朋友可以参考下...

    果冻想5492021-02-04
  • C/C++C++中简单的文本文件输入/输出示例详解

    C++中简单的文本文件输入/输出示例详解

    C++程序把输入和输出看作字节流,输入时程序从输入流中抽取字节,输出时程序将字节插入到输出流中,下面这篇文章主要给大家介绍了关于C++中简单的文本文...

    francis_xd5862022-07-20
  • C/C++C语言实现简单的三子棋

    C语言实现简单的三子棋

    这篇文章主要为大家详细介绍了C语言实现简单的三子棋,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    一颗苹果.3672021-10-06
  • C/C++C语言实现学生宿舍信息管理系统课程设计

    C语言实现学生宿舍信息管理系统课程设计

    这篇文章主要为大家详细介绍了C语言实现学生宿舍信息管理系统课程设计,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参...

    tan-121011092022-10-21