第八章——善于利用指针(六)

原创
2020/12/20 13:21
阅读数 77

动态内存分配与指向它的指针变量

  • 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头文件包含到程序文件中

  1. 用malloc函数开辟动态存储区
  • (1)其函数原型为void * malloc(unsigned int size);
  • (2)其作用是在内存的动态存储区中分配一个长度为size的连续空间。
  • (3)形参size的类型定位无符号整数(不允许为负数)。
  • (4)此函数的值(即“返回值”)是所分配区域的第一个字节。
  • (5)注意指针的基类型为void,即不指向任何类型的数据,只提供一个纯地址。
  • (6)如果此函数未能成功执行(例如内存空间不足),则返回空指针(NULL)。

malloc(100); //开辟100字节的临时分配域,函数值为其第1个字节的地址

  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

  1. 用realloc函数重新分配动态存储区
  • (1)其函数原型void * realloc(void * p,unsigned int size);
  • (2)如果已经通过malloc函数或calloc函数获得了动态空间,需要改变其大小,可以用realloc函数重新分配
  • (3)用realloc函数将p所指向的动态空间的大小改变为size。p的值不变。如果充分配不成功,返回NULL

realloc(p,50); //将p所指向的已分配的动态空间改为50字节

  1. 用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));

因为在进行编译时,系统可以自动进行隐式的转换,而不必人为地显式的强制类型转换

内存的动态分配主要应用于建立程序中的动态数据结构(如链表)中。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部