“教小白精通编程”系列之“C语言教程” (版权所有,不得转载,擅自抄袭转载将承担法律责任)
动态内存分配
下面的这个学生成绩输入输出程序最多只能输入100个学生成绩,假如多于100个学生的情况怎么办?
struct student{
char name[10];
double score;
}
void Print(struct student stu){
printf("%s,%lf\n",stu.name,stu.score);
}
void Scanf(struct student *stu){
scanf("%s %lf",&(stu->name),&(stu->score));
}
void main(){
sturct student students[100]; /*最多能放100个学生成绩*/
int n=0; /*实际学生人数*/
do{
Scanf(&students[n]);
if(s.score<0) break; /*推出循环*/
n++; //新输入一个学生信息,人数增加1
}while(1);
for(int i = 0 ; i<n ;i++)
Print(students[i])
}
解决方法:可以根据实际情况来分配存放所有学生成绩的内存块。 即调用内存分配函数如malloc向操作系统申请足够大的内存空间。
使用完内存后,假如不再需要,应该调用内存释放函数如 free将这块内存还给操作系统! “有借有还”。
内存分配函数在
主要有4个内存分配和释放函数,如下:
- void *calloc(int num, int size);
分配num个元素,每个元素占据字节数为size的内存块,返回该内存块的地址
- void free(void *address);
释放 address 指向的内存块.
- void *malloc(int num);
分配num个字节的内存块,返回该内存块的地址
- void *realloc(void *address, int newsize);
重新分配newsize字节的内存空间,并将address指针变量指向的旧内存块的数据转移到新分配的内存块中.返回新内存块的地址
学生成绩单程序可以修改如下:
struct student{
char name[10];
double score;
}
void Print(struct student stu){
printf("%s,%lf\n",stu.name,stu.score);
}
void Scanf(struct student *stu){
scanf("%s %lf",&(stu->name),&(stu->score));
}
void main(){
sturct student *students; /*sturct student类型的指针变量*/
int n=0; /*实际学生人数*/
scanf("%d",&n); /*从键盘输入实际的学生人数*/
/*分配n个sturct student类型元素的内存块,
并将返回地址转化为“sturct student类型的指针”,
其中的sizeof用于查询一个sturct student类型变量占用的字节数。
比如,sizeof(int)用于得到一个int类型变量占用内存的字节数
*/
students = (sturct student *) malloc(n*sizeof(sturct student));
do{
Scanf(&students[n]);
if(s.score<0) break; /*推出循环*/
n++; //新输入一个学生信息,人数增加1
}while(1);
for(int i = 0 ; i<n ;i++)
Print(students[i]);
free(students); /*释放students指向的内存块*/
}
链表
存储上面的学生成绩表,还有一种很好的方法,就是为每个学生数据动态分配一块内存块,表示不同学生数据的内存块之间用指针变量关联起来。即第1个学生的内存块中有一个存储第2个学生内存块的地址,第2个学生的内存块中有一个存储第3个学生内存块的地址,如此下去,如同一个链条一样,将这些学生的内存块串在一起。这种存储一组数据元素的方式称之为“链表”,而表示每个学生数据的内存块称为一个”结点“。
我们不需要给每个学生的”结点“起一个名字,只要将第一个学生的”结点“保存在一个命名的指针变量,然后就可以通过这指针变量(比如叫做head)依次访问到每个”结点“。
我们可以用这个链表存储维护所有学生的数据,并执行各种操作比如:插入、删除、查找等。
上述链表中,除第一个学生外,其他学生结点的前面必然有一个”前驱结点”, 因此对这种链表进行操作比如删除一个结点,需要区分一个结点是否是第一个结点而执行不同的处理逻辑。为了使得对所有学生结点的操作能否统一起来,我们可以认为的增加一个没有实际数据的“结点”,将这个所谓的“头结点”放在最前面,它后面跟着的就是第1个学生的结点(称之为”首结点“)。即用如下形式的链表。 我们可以针对这种链表写一组辅助函数来对维护这个链表中的数据,比如初始化、插入、 删除、查找等.详细的实现代码可以参考
只要将其中的数据类型T换成学生数据类型就可以了。比如
//用typedef 给无名结构类型起了一个新名字叫做T,
// 或者说T就是表示一个学生信息的结构类型。
typedef struct{
char name[10]
float score;
} T;
您的打赏是对我最大的鼓励!