一、进程和进程空间:
1、什么是进程:
指程序加载到内存后开始执行,到执行结束这样一段时间概念。对同一个程序,每打开一次该程序都会拉起一个新进程,关闭一个则结束一个进程。(程序是静态概念,而进程是动态概念)
2、进程空间:
程序运行起来就成为一个进程,进程所占用的空间就是进程空间。我们来看一下,进程空间是什么样的。
3、栈(Stack):
栈中可存放任意类型的变量,但必须是 auto 修饰的局部变量。
特点:随用随开,用完即消。内存的分配和销毁系统自动完成。
栈并不大,它的意义并不在于存储大数据,而在于数据交换。
栈溢出:局部变量太多、太大,或者递归层数太多。
4、堆(Heap):
特点:堆可以存放任意类型的数据,但需要自己申请与释放。
堆的大小相对于栈来说很大,但实际使用中,受限于实际内存的大小和内存是否连续。
二、内存管理:
安全五步法:申请、判空、使用、释放、置空。
1、申请:malloc()
2、扩容(缩容):realloc()
扩容会出现两种情况:
1)原内存后面空间足够,则直接在原内存后面开辟空间扩容;
2)原内存后面空间不足,此时会找一块新的大空间,并将原内存内容拷贝过来,然后扩容达到目的。
缩容后,原空间被缩小部分的内存中的内容将不可用。
3、释放:free()
使用示例如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *p = (char *)malloc(10 * sizeof(char)); //申请
if (p == NULL) //判空
{
printf("error");
exit(-1);
}
strcpy(p, "china"); //使用
printf("start : %s\n", p);
p = (char *)realloc(p, 20 * sizeof(char)); //扩容
if (p == NULL) //再次判空
{
printf("error");
exit(-1);
}
strcpy(p, "hello china"); //12个字节
printf("after : %s\n", p);
free(p); //释放
p = NULL; //置空
getchar();
return 0;
}
4、应用----以动态数组为例:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int len;
printf("start len :");
scanf("%d", &len);
int oldLen = len;
int *p= (int *)malloc(len * sizeof(int)); //原空间
if (p == NULL)
{
printf("error");
exit(-1);
}
//memset(p, 0, len * sizeof(int));每个字节初始化为零
for (int i = 0; i < len; i++)
{
p[i] = 100 + i;
printf("%d\n",p[i]);
}
printf("after len : ");
scanf("%d", &len);
p = (int *)realloc(p,len * sizeof(int)); //扩大(缩小)
if (p == NULL)
{
printf("error");
exit(-1);
}
for (int i = 0; i < len; i++)
{
if (i>=oldLen)
{
p[i] = 200 + i;
}
printf("%d\n", p[i]);
}
free(p);
p = NULL;
system("pause");
return 0;
}
三、总结:
C语言内存管理,是一个重要话题,值得深入探讨。防止内存泄漏,至关重要。
常见错误:
1、malloc和free不配对:导致多次申请或多次释放出错。
2、忽略置空与判空:释放以后指针未置为 NULL再次作判空使用或释放以后继续非法使用。
......
char*p=(char*)malloc(100);
strcpy(p,"hello");
free(p); // 此时p所指的内存被释放,但是p所指的地址仍然不变
//p = NULL;忘了此句,后而又用到了p
......
if(NULL!=p) //没有起到防错作用
{
strcpy(p,"hello"); //出错
}