动态内存分配与指向它的指针变量
- 8.8 动态内存分配与指向它的指针变量
- 8.8.1 什么是内存的动态分配
- 8.8.2 怎样建立内存的动态分配
- 使用malloc函数开辟动态存储区
- 用calloc函数开辟动态存储区
- 用realoc函数重新分配动态存储区
- 用free函数释放动态存储区
- 8.8.3 void指针类型
内存的动态分配概念
- 全局变量是分配在内存中的静态存储区的,非静态的局部变量(包括形参)是分配在内存中的动态存储区的。
这个存储区是一个称为栈stack的区域。 - 同时,C语言还允许内存动态分配区域,以存放一些临时用的数据,这些数据不必在程序的声明部分定义,也不必等到函数结束时才释放,而是需要时随时开辟,不需要时随时释放。
这些数据是临时存放在一个特别的自由存储区,称为堆heap区。 可以根据需要,向系统申请所需大小的空间。
因为未在声明部分定义它们为变量或数组,因此不能通过变量名或数组名去引用数据,只能通过指针来引用
建立内存的动态分配的方法(4种)
malloc、calloc、realloc、free这四个函数的声明在stdlib.h头文件中,在用到这些函数时应当用"#include <stdlib.h>“指令把stdlib.h头文件包含到程序文件中
- 用malloc函数开辟动态存储区
- (1)其函数原型为
void * malloc(unsigned int size);
- (2)其作用是在内存的动态存储区中分配一个长度为size的连续空间。
- (3)形参size的类型定位无符号整数(不允许为负数)。
- (4)此函数的值(即“返回值”)是所分配区域的第一个字节。
- (5)注意指针的基类型为void,即不指向任何类型的数据,只提供一个纯地址。
- (6)如果此函数未能成功执行(例如内存空间不足),则返回空指针(NULL)。
malloc(100); //开辟100字节的临时分配域,函数值为其第1个字节的地址
- 用calloc函数开辟动态存储区
- (1)函数原型为
void * calloc(unsigned n,unsigned size)
- (2)其作用是在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大, 足以保存一个数组
- (3)用calloc函数可以为一维数组开辟动态空间,n为数组元素个数,每个元素长度为size。这就是动态数组。
- (4)函数返回指向所分配域的第一个字节的指针;如果分配不成功,返回NULL
p = calloc(50,4); //开辟50 * 4个字节的临时分配域,把首地址赋给指针变量p
- 用realloc函数重新分配动态存储区
- (1)其函数原型
void * realloc(void * p,unsigned int size);
- (2)如果已经通过malloc函数或calloc函数获得了动态空间,需要改变其大小,可以用realloc函数重新分配
- (3)用realloc函数将p所指向的动态空间的大小改变为size。p的值不变。如果充分配不成功,返回NULL
realloc(p,50); //将p所指向的已分配的动态空间改为50字节
- 用free函数释放动态存储区
- (1)其函数原型为
void free(void * p);
- (2)其作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。
- (3)p应是最近一次调用calloc或malloc函数时得到的函数返回值
- (4)free函数没有返回值
free(p); //释放指针变量p所指向的已分配的动态空间
void指针类型
- 在以前的C版本(包括C89)中,函数返回的地址一律指向字符型数据,即得到
char *
型指针。
C99允许使用基类型为void的指针类型。可以定义一个基类型为void的指针变量(即void *
型变量),它不指向任何类型的数据 - (1)指向void类型应理解为能指向任何类型的数据,而不是指向空类型或不指向确定的类型的数据
- (2)在将它的值赋给另一指针变量时由系统对它进行类型转换,是指适合于被赋值的变量的类型(用到强制类型转换)
- (3) 有类型的变量与
void *
类型的数据相互之间不能直接赋值,必须进行强制类型转换 - (4)
void *
型指针代表无指向的地址,这种指针不指向任何类型的数据。不能企图通过它存取数据,在程序中它只是过渡性的,只有转换为有指向的地址,才能存取数据
现在所用的一些编译系统在进行地址赋值时,会自动进行类型转换
int * pt;
pt = (int *)malloc(100); // malloc(100)是void * 类型,把它转换为int *型
可以简化为
pt = malloc(100); // 自动类型转换
赋值时,系统会先把malloc(100)转换为pt的类型,即(int *)型,
然后赋给pt,这样pt就指向存储区的手自截,在其指向的存储单元中可以存放整型数据
这种指针称为空类型指针(typeless pointer),它不指向任一种具体的类型数据,只提供一个纯地址。这是C有关地址应用的一种特殊情况。
建立内存动态分区和使用void指针的例题
建立动态数组,输入5个学生的成绩,另外用一个函数检查其中有无低于60分的,输出不合格的成绩
解题思路:用malloc函数开辟一个动态自由区域,用来存5个学生的成绩,
会得到这个动态域第1个字节的地址,它的基类型是void型。
用一个基类型为int的指针变量p来指向动态数组的各元素,并输出它们的值。
但必须先把malloc函数返回的void指针转换为整型指针,然后赋给p1
#include <stdio.h>
// 程序中用了malloc函数,应包含stdlib.h
#include <stdlib.h>
int main()
{
//函数声明
void check(int *);
// p1是int指针
int *p1,i;
// 开辟动态内存区,将地址转换成int * 型,然后放在p1中
p1 =(int *)malloc(5 * sizeof(int));
for(i = 0;i < 5;i++)
{
// 输入5个学生的成绩
scanf("%d",p1 + i);
}
// 调用check函数
check(p1);
return 0;
}
void check(int * p)
{
int i;
printf("They are fail: ");
for(int = 0;i < 5;i++)
{
// 输出不合格的成绩
if(p[i] < 60) printf("%d",p[i]);
}
printf("\n");
}
程序分析:
- 在程序中没有定义数组,而是开辟一段动态自由分配区,作为动态数组使用
- 在调用malloc函数时没有给出具体的数值,而是用
5* sizeof(int)
,
因为有5个学生的成绩,每个成绩是一个整数,
但在不同的系统中存放一个整数的字节数是不同的,为了使程序具有通用性,故用sizeof运算符测定在本系统中整数的字节数 - 调用malloc函数的返回值是
void *
型的,要把它赋给p1,应先进行类型转换,把该指针转换成int *
型。 - 用for循环输入5个学生的成绩,注意不是用数组名,而是按照地址法计算出相应的存储单元的地址,然后分别赋值给动态数组的5个元素
- 调用check函数时把p1的值传给形参p,因此形参p也指向动态区的第1个数据。
- 最后使用free函数释放动态分配区
- 第6行也可以直接写成
p1 = malloc(5 * sizeof(int));
因为在进行编译时,系统可以自动进行隐式的转换,而不必人为地显式的强制类型转换。
内存的动态分配主要应用于建立程序中的动态数据结构(如链表)中。