动态内存分配
1,说明
在C程序中,不同数据类型在内存中分配说明:
1)全局变量 -----内存中的静态存储区
2)非静态的局部变量-----内存中的动态存储区—stack栈
3)临时使用的数据----建立动态内存分配区域,需要时随时开辟,不需要时及时释放 —heap堆
4)根据需要向系统申请所需大小的空间,由于未在声明部分定义其为变量或者数组,不能通过变量或者数组名来引用这些数据,只能通过指针来引用
2.内存动态分配的相关函数
1)头文件 #include <stdio.h>声明了内存动态分配的函数
2)函数原型void *malloc(usigned int size)// memory allocation
- 作用----在内存的动态存储区(堆区)中分配一个长度为size的连续空间。
- 形参size的类型为无符号整型,函数返回值时所分配区域的第一个字节的地址,即此函数是一个指针型函数,返回的指针指向该分配域的开头位置
- malloc(100);开辟100字节的临时空间,返回值为其第一个字节的地址
3)函数原型void *calloc(usigned n,unsigned size)
- 作用-----在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组
- 用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size
- 函数返回指向所分配域的起始位置的指针:分配不成功,返回NULL
- p=calloc(50,4);//开辟50*4个字节临时空间,把起始地址分配给指针变量p
4)函数原型:void free (void *p)
- 作用-----释放变量p所指向的动态空间,使这部分空间能重新被其他变量使用。
- p是最近一次调用calloc或malloc函数时的函数返回值
- ‘free函数无返回值】
- free§;//释放p所指向的已分配的动态空间
5)函数原型 void *realloc (void *p,unsigned int size)
- 作用----重新分配malloc或calloc函数获得的动态空间大小,将p指向的动态空间大小改变为size,p的值不变,分配失败返回NULL
- realloc(p,50);//将p所指向的已分配的动态空间 改为50字节
6)返回类型说明
C99标准把以上malloc,calloc,realloc函数的基类型定义为void类型,这种指针称为无类型指针(typeiess pointer),即不指向哪一种具体的类型数据,只表示用来指向一个抽象的类型的数据,即仅提供一个纯地址。而不能指向任何具体的对象。
3.void指针类型
C99允许使用基类型为void的指针类型。可以定义一个基类型为void的指针变量(即void *型变量),它不指向任何类型的数据。请注意:不要把“指向void类型”理解为能指向“任何类型“的数据,而应理解为“指向空类型”或“不指向确定的类型”的数据
。在将它的值域赋给另一指针变量时由系统对他进行类型转换,使之适合于被赋值的变量的类型例如:
int a=3;//定义a为整形变量
int *pr=&a;//pr指向int型变量
char *p2; //p2指向char型变量
void *p3;//p3为无类型指针变量(基类为void型)
p3=(void *)p1;//将p1的值转换为void*类型,然后赋值给p3
p2=(char *)p3; //将p3的值转换为void *类型,然后赋值给p2
printf("%d",*p1);//合法输出a的值
p3=&a;
printf("%d",*p3);//错误p3是无指向的,不能指向a
注意:如果这个指针变量p是void类型,那么就不可以使用 * p去取得p所指向的值
说明:当把void指针赋值给不同基类型的指针变量(或相反)时,编译系统会自动进行转换,不必用户自己进行强制转换。例如:
p3=&a;
相当于“p3=(void*)&a”,赋值后p3得到a的纯地址,但并不指向a,不能通过*p3输出a的值
4.应用实例
动态创建数组,输入5个学生的成绩,另外一个函数检测成绩低于60分的,输出不合格的成绩
#include <stdio.h>
#include <stdlib.h>
int main() {
void check(int *);//声明函数check(函数定义写在下面就需要进行声明操作)
int *p,i;
//在堆区开辟一个5*4的空间,并将地址(void *)转成(int *),赋给p
p=(int*)malloc(5*sizeof (int));//void* --》int *[c99];20个字节5个int
for(i=0;i<5;i++){ //for循环5次
scanf("%d ",p+i);
}
check(p);
free(p);//销毁堆区p指向的空间
getchar();
getchar();
return 0;
}
void check(int *p){
int i;
printf("不合格的成绩有:\n");
for(i=0;i<5;i++){
if(p[i]<60){//成绩小于60就输出(因为p是一个连续指针变量可以直接通过p[i]取到相应的值)
printf("%d ",p[i]);
}
}
}
//运行结果:
12 78 56 45 77 25
不合格的成绩有:
12 56 45
Process finished with exit code 0
代码内存图:
5.动态分配内存的基本原则
1)避免分配大量的小内存块。分配堆上的内存有一些系统开销,所以分配许多小的内存块比分配几个大的内存块的系统开销大
内存泄漏:是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果
2)仅在需要时分配内存。只要使用完堆上的内存块,就需要及时释放它,(如果使用动态分配内存,需要遵守原则:谁分配,谁释放)否则可能出现内存泄漏
3)总是确保释放已分配的内存。在编写分配内存的代码时,就要确定在代码的什么地方释放内存
4)在释放内存之前,确保不会无意中覆盖堆上已分配的内存地址,否则程序就会出现内存泄漏。在循环中分配内存时,要特别小心
5)指针使用一览
变量定义 | 类型提示 | 含义 |
---|---|---|
int i; | int | 定义整型变量 |
int *p; | int * | 定义p为指向整型数据的指针变量 |
int a[5] | int [5] | 定义整型数组a,他有5个元素 |
int *p[4] | int *[4] | 定义指针数组p,它有4个指向整型数据的指针元素组成 |
int (*p)[4]; | int(*)[4]; | p为指向包含4个元素的一维数组的指针变量(这是p指向的不是一个变量二十一个数组了) |
int f(); | int () | f为返回整型函数值的函数 |
int *p(); | int *() | p为返回一个指针的函数,该指针指向整型数据 |
int (*p)(); | int(*)() | p为指向函数的指针,该函数返回一个整形值(函数指针) |
int **p; | int ** | p是一个指针变量,它指向一个指向整型数据的指针变量 |
int (*p)(); | int(*)() | p为指向函数的指针,该函数返回一个整形值(函数指针) |
int **p; | int ** | p是一个指针变量,它指向一个指向整型数据的指针变量 |
void *p; | void * | p是一个指针变量,基类型为void(空类型),不指向具体的对象 |