C语言之内存管理初探:栈和堆、malloc、calloc、realloc、free

一. 进程空间

在这里插入图片描述

二.进程空间图示

  程序被加载到内存以后,0-4G的内存空间。这0-4G内存中的布局如下图:
在这里插入图片描述

三. 栈内存

1.栈存储的特点:
在这里插入图片描述
  栈的大小并不大,他的意义并不在于存储大数据,而在于数据交换。

例:
在这里插入图片描述
2.常见栈溢出原因:局部变量过多,过大;或递归层数太多。
在这里插入图片描述

四. 堆内存

1.堆存储的特点:堆内存可以存放任意类型的数据,但需要自己申请与释放。
2.堆大小,想象中的无穷大(对于栈来说),大空间申请,唯此,无它耳。但实际使用中,受限于实际内存的大小和内存是否连续性。
3.堆内存的申请与释放:
在这里插入图片描述
例1:malloc
在这里插入图片描述
附例1代码:

//小问学编程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main()
{
    
    
	//申请基本数据类型和数组,栈堆空间作对比
	int a;
	int* p=&a;
	a=100;
	printf("*p=%d\n",a);
	
	int* pm=(int*)malloc(sizeof(int));
	if(pm==NULL)
		return -1;
	
	*pm=100;
	printf("pm=%d\n",*pm);
	//申请基本数据类型和数组,栈堆空间作对比
	int array[10];
	int* pa=array;
	pm=(int*)malloc(10*sizeof(int));
	//memset(pm,0,10*sizeof(int));
	//memset(pm,0,10*sizeof(int));此时会导致什么样的结果呢?
	for(int i=0;i<10;i++)
	{
    
    
		printf("%d\n",pm[i]);
	}
	free(pm);
	return 0;
}

附:memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
在这里插入图片描述
补充:
在这里插入图片描述
例2:calloc
在这里插入图片描述
附例2代码:

//小问学编程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main()
{
    
    
	int* array=(int*)calloc(10,sizeof(int));
	
	for(int i=0;i<10;i++)
	{
    
    
		printf("%d\n",array[i]);//已被初始化
	}
	
	return 0;
}

例3:realloc
在这里插入图片描述
附例3代码:

//小问学编程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main()
{
    
    
    int* array=(int*)calloc(10,sizeof(int));
    int* newArray=realloc(array,30);
    //array=realloc(array,30);
    if(newArray==NULL)
    {
    
    
        printf("realloc 失败\n");
        return -1;
    }
    for(int i=0;i<20;i++)
    {
    
    
        printf("%d\n",newArray[i]);
    }

	return 0;
}

free
在这里插入图片描述
例4:free
在这里插入图片描述
附例4代码:

//小问学编程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main()
{
    
    
    int* array=(int*)calloc(10,sizeof(int));
    free(array);
	return 0;
}

五. 应用模型

例5:之前VLA变长数组,在运行时,只有一次初始化其长度的机会,且容易发生栈溢出。现在有了堆空间的动态申请与释放,动态数组,就成了真正意义上的动态数组了。
在这里插入图片描述
附例5代码:

//小问学编程
#include <stdio.h>

int main()
{
    
    
	int len;
	printf("pls new len:");
	scanf("%d",&len);
	
	int* p=(int*)realloc(NULL,sizeof(int)*len);
	for(int i=0;i<len;i++)
	{
    
    
		printf("%d\n",p[i]);
	}
	//加大
	printf("pls new len:");
	scanf("%d",&len);
	
	p=(int*)realloc(p,sizeof(int)*len);
	for(int i=0;i<len;i++)
	{
    
    
		printf("%d\n",p[i]);
	}
	//变小
	printf("pls new len:");
	scanf("%d",&len);
	
	p=(int*)realloc(p,sizeof(int)*len);
	for(int i=0;i<len;i++)
	{
    
    
		printf("%d\n",p[i]);
	}
	free(p);
	return 0;
}

六. 常见错误案例剖析

在这里插入图片描述
6.1 置空与判空
在这里插入图片描述
例:
在这里插入图片描述
6.2 重复申请
在这里插入图片描述
6.3 谁申请谁释放模型(并非绝对)
在这里插入图片描述

七. 开放的地址空间

  传对象的地址到不同的作用域,可依据地址修改地址所指向对象的内容,根本原因是地址空间是开放的。

八. 栈空间不可以返回

例6:
在这里插入图片描述

附例6代码:

//小问学编程
#include<stdio.h>
//1.数值是可以返回的
//2.地址也是可以返回的
//3.栈上的空间不可以返回,原因:随用隋开,用完即消
//4.堆上的空间,是可以返回的。
int func()
{
    
    
    int a=500;
    return a;
}
int* foo()
{
    
    
    int b=400;
    int *pa=&b;
    printf("&a=%p\n",pa);
    return pa;
}

int *func2()
{
    
    
    int arr[100];
    return arr;
}

int main()
{
    
    
    int a=func();
    printf("a=%d\n",a);
    int* pa=foo();
    printf("pa=%p\n",pa);
    printf("%d\n",*pa);
    *pa=300;

    return 0;
}

九. 堆空间可以返回

例7:
在这里插入图片描述
附例7代码:

//小问学编程
#include<stdio.h>
//1.数值是可以返回的
//2.地址也是可以返回的
//3.栈上的空间不可以返回,原因:随用隋开,用完即消
//4.堆上的空间,是可以返回的。
char* getFormatMem(int size,char content)
{
    
    
    char* p=(char*)malloc(size *sizeof(char));
    if(NULL==p)
        exit(-1);
    memset(p,content,size *sizeof(char)-1);
    p[size *sizeof(char)-1]='\0';
    return p;
}
int main()
{
    
    
    char* p=getFormatMem(20,'a');
    printf("p=%s\n",p);
    free(p);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43297891/article/details/113417477