目录
1.什么是内存的动态匹配
之前介绍过全局变量和局部变量,全局变量是分配在内存中的静态存储区的,非静态的局部变量(包括形参)是分配在内存中的动态存储区的,这个存储区称为 栈。除此以外,C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据不必在程序的声明部分定义,也不必等到函数结束时才释放,而是需要时随时开辟,不需要时随时释放。这些数据是临时存放在一个特别的自由存储区,称为 堆 区。可以根据需要,向系统申请所需大小的空间。由于未在声明部分定义它们为变量或数组,因此不能通过变量名或数组名去引用这些数据,只能通过指针引用。
2. 怎样建立内存的动态分配
对内存的动态分配是通过系统提供的库函数来实现的,主要有malloc,calloc,free,realloc 这4个函数
2.1. 用malloc函数开辟动态存储区
其函数原型为 void * malloc(unsigned int size ) ;
1.如果开辟成功,则返回一个指向开辟好的空间的指针;
2.如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查,不然可能会造成野指针的问题;
3.返回值的类型时void*,所以malloc函数并不知道开辟空间的类型,具体-+在使用的时候由使用者自己来决定返回值的类型;
4.如果参数size为0,此时malloc函数的行为是标准未定义的,取决于程序运行时使用的编译器;
malloc(100); //开辟100字节的临时分配域,函数值为其第1个字节的地址
注意指针的基类型为void,即不指向任何类型的数据,只提供一个纯地址。如果此函数未能成功执行,则返回空指针(NULL)。
malloc的使用:
vint main()
{
int* p = (int*)malloc(40);//向内存申请一块40字节的空间,并对放回的指针类型转换
if (p == NULL)//返回NULL指针时让打印错误信息并让程序结束
{
perror("malloc");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
return 0;
}
这样就对开辟的内存空间进行了一个使用了,但是还有点问题,因为向内存申请的空间没有进行释放。
所以这时就引入了另一个函数free
2.2 用free函数释放动态存储区
其函数原型为:void free (void * p);
1.如果参数p指向的空间是不是动态开辟的,那么free函数的行为是未定义的;
2.如果参数 p是NULL指针,则free函数什么事也不做;
free函数的使用:
#include<stdio.h>
#include<stdlib.h>
#include<cassert>
int main() {
int* p = (int*)malloc(sizeof(int)*10);
assert(p != NULL);
for (int i = 0; i < 10; i++) {
printf("%15d", p[i]);
}
free(p); //释放内存
p = NULL;
}
2.3 用calloc函数开辟动态存储区
其函数原型为:void * calloc(unsigned n,unsigned size);
1.calloc的功能是为num个字节大小为size的元素开辟一个空间,并且把空间的每个字节的数据都初始化为0,然后返回这块连续空间的起始位置的地址;
2.与malloc函数的区别只在于,calloc在返回地址之前会把申请的空间的每个字节的数据都初始化为全0;
calloc函数的使用:
#include<stdio.h>
#include<stdlib.h>
#include<cassert>
int main() {
int* p = (int*)calloc(10, sizeof(int));
assert(p != NULL);
for (int i = 0; i < 10; i++) {
printf("%5d", p[i]);
}
free(p);
p = NULL;
}
2.4 用relloc函数重新分配动态存储区
其函数原型为:void * relloc(void*p,unsigned int size);
1.p是要调整的内存空间;
2.size是调整之后的新大小;
3.返回值为调整之后的内存起始位置;
4.realloc函数在调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间;
relloc函数的使用:
#include<stdio.h>
#include<stdlib.h>
#include<cassert>
#include<string.h>
int main() {
int* p = (int*)calloc(2 , sizeof(int));
assert(p != NULL);
int* q = (int*)realloc(p,5 * sizeof(int));
assert(q != NULL);
}
3.常见的动态内存错误
3.1 对非动态内存使用free
对栈区上的空间使用free:
int main()
{
int a = 0;
free(&a); // 错误
return 0;
}
3.2 使用释放一块动态开辟内存的一部分
int main()
{
int* p = (int*)malloc(40);
p++; // 此时p没有指向动态开辟内存的起始位置
free(p);
return 0;
}
3.3 对同一块动态内存多次释放
int main()
{
int* p = (int*)malloc(40);
free(p);
free(p); //对同一块内存多次释放
return 0;
}
3.4 动态开辟内存忘记释放
int main()
{
while (1)
{
malloc(40);
}
return 0;
}
没有free,如果程序在没有结束之前申请的内存都没有进行释放的话,就会出现内存泄漏的问题。所以在申请好一块内存之后要记得对其进行释放。
4.堆区与栈区的区别:
C语言的内存模型分为5个区:栈区、堆区、静态区、常量区、代码区。每个区存储的内容如下:
1、栈区:存放函数的参数值、局部变量等,由编译器自动分配和释放,通常在函数执行完后就释放了,其操作方式类似于数据结构中的栈。栈内存分配运算内置于CPU的指令集,效率很高,但是分配的内存量有限,比如iOS中栈区的大小是2M。
2、堆区:就是通过new、malloc、realloc分配的内存块,编译器不会负责它们的释放工作,需要用程序区释放。分配方式类似于数据结构中的链表。“内存泄漏”通常说的就是堆区。
3、静态区:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后,由系统释放。
4、常量区:常量存储在这里,不允许修改。
5、代码区:顾名思义,存放代码。