一. 进程空间
二.进程空间图示
程序被加载到内存以后,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;
}