C语言结构体变量与链表知识总结.docx
《C语言结构体变量与链表知识总结.docx》由会员分享,可在线阅读,更多相关《C语言结构体变量与链表知识总结.docx(24页珍藏版)》请在冰豆网上搜索。
![C语言结构体变量与链表知识总结.docx](https://file1.bdocx.com/fileroot1/2023-5/17/f74ad1ce-d6d2-4652-b626-9db4a112b0df/f74ad1ce-d6d2-4652-b626-9db4a112b0df1.gif)
C语言结构体变量与链表知识总结
结构体与链表
11.1结构体类型的定义
结构体是由C语言中的基本数据类型构成的、并用一个标识符来命名的各种变量的组合,其中可以使用不同的数据类型。
1.结构体类型的定义
Struct结构体名
{类型标识符1成员名1;
类型标识符2成员名2;
……
类型标识符n成员名n;
};
Struct结构体名——结构体类型名
2.关于结构体类型的说明:
(1)“struct结构体名”是一个类型名,它和int、float等作用一样可以用来定义变量。
(2)结构体名是结构体的标识符不是变量名,也不是类型名。
(3)构成结构体的每一个类型变量称为结构体成员,它像数组的元素一样,单数组中元素以下标来访问,而结构体是按结构体变量名来访问成员的。
(4)结构体中的各成员既可以属于不同的类型,也可以属于相同的类型。
(5)成员也可以是一个结构体类型,如:
Structdate
{
Intmonth;
Intday;
Intyear;
};
Structperson
{
Floatnum;
Charname[20];
Charsex;
Intage;
Structdatebirthday;
Charaddress[10];
};
11.2结构体类型变量
11.2.1结构体类型变量的定义
1.先定义结构体类型,再定义结构体变量
形式:
Struct结构体名
{类型标识符1成员名1;
类型标识符2成员名2;
……
类型标识符n成员名n;
};
Struct结构体名变量名表;
例如:
Structstudent
{charname[20];
Charsex;
Intage;
Floatscore;
};
Structstudentstu1,stu2;
2.在定义结构体类型的同时定义变量
形式:
Struct结构体名
{
类型标识符1成员名1;
类型标识符2成员名2;
……
类型标识符n成员名n;
}变量名表;
例如:
Structstudent
{
Charname[20];
Charsex;
Intage;
Floatscore;
}stu1,stu2;
3.用匿名形式直接定义结构体类型变量
形式:
Struct
{
类型标识符1成员名1;
类型标识符2成员名2;
……
类型标识符n成员名n;
}变量名表;
例如:
Struct
{
Charnaem[20];
Charsex;
Intage;
Floatscore;
}stu1,stu2;
11.2.2结构体变量的使用
结构体是一种新的数据类型,因此结构体变量也可以像其它类型的变量一样赋值、运算,不同的是结构体变量以成员作为基本变量。
结构体成员的表示方式为:
结构体变量名.成员名
其中的圆点运算符称为成员运算符,它的运算级别最高。
如果将“结构体变量名.成员名”看成一个整体,则这个整体的数据类型与结构体中该成员的数据类型相同,这样就可以像前面所讲的变量那样使用,但应遵循以下规则:
(1)不能将一个结构体变量作为一个整体进行输入和输出,而只能对结构体变量中的各个成员分别进行输入和输出。
Structdate
{
Intmonth;
Intday;
Itnyear;
}day;
Scanf(“%d%d%d”,day);(错误)
Scanf(“%d%d%d”,&day.year,&day.month,&day.day);(正确)
Printf(“%d%d%d”,day);(错误)
Printf(“%d%d%d”,day.year,day.month,day.day);(正确)
(2)如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级地找到最底的一级的成员,只能对最底级的成员进行赋值或存取运算。
Structdate
{
Intmonth;
Intday;
Intyear;
};
Structstudent
{
Longnum;
Charnaem[20];
Charsex;
Intage;
Structdatebirthday;
Chardepart[10];
}stu1;
如:
stu1.birthday.year=2004;
Stu1.birthday.month=12;
(3)对结构体变量的成员可以像同类型普通变量一样进行各种运算。
11.2.3结构体变量的初始化与存储
1.结构体变量的初始化:
在定义结构体变量的同时给它赋以初值。
Structstudent
{
Charname[20];
Charsex;
Intage;
Floatscore;
}stu1,stu2={“wangwu”,’m’,20,88.5};
2.结构体变量所占内存的字节数
·struct类型用内存字节数=?
·是所有成员变量的内存总和吗?
用运算符sizeof获得结构体大小
Sizeof(变量或表达式)
Sizeof(类型)
Typedefstructsample
{
Charm1;
Intm2;
Charm3;
}SAMPLE;
Printf(“%d\n”,sizeof(structsample));
Printf(“%d\n”,sizeof(SAMPLE));
并非所有成员变量的内存总和
事实上,所有数据类型在内存中都是从偶数地址开始存放的且结构所占的实际空间一般是按照机器字长对齐的不同编译器、平台,对齐方式会有变化
结构体变量的成员存储对齐规则是与机器相关的
具有特定数据类型的数据项大小也是与机器相关的
所以一个结构体在内存中的存储格式也是与机器相关的
3.结构体变量存储分配示意图
11.3结构体类型数组
11.3.1结构体数组的定义与使用
结构体是一种新的数据类型,同样可以有结构体数组。
1、结构体数组的定义
结构体数组就是具有相同结构体类型的变量集合。
Struct结构体名
{
类型标识符1成员名1;
类型标识符2成员名2;
……
类型标识符n成员名n;
}数组名[整型常量表达式];
加入要定义一个班级100个同学的学号、姓名、性别、年龄,可以定义成一个结构体数组。
如下所示:
Struct{
Longnum;
Charname[20];
Charsex;
Intage;
}stu[100];
11.3.2结构体数组的初始化
一般形式是在定义的数组后面加上={初始值表列};
Structstudent
{
Longnum;
Charname[20];
Charsex;
Intage;
Floatscore;
Charadd[5];
}stu[3]={
{101,”WGJ”,’M’,28,88.5,”CS”}
{102,”DYH”,’F’,26,88.0,”CS”}
{103,”DYC”,’M’,24,78.5,”CS”}
};
11.3.3结构体数组的使用
结构体数组成员的访问十一数组元素为结构体变量的,其形式为:
结构体数组元素名.成员名
如:
stu[2].age
【例11.1】候选人得票的统计。
设有三个候选人,每次输入一个得票的候选人名字,要求最后输出各人得票结果。
Structperson
{
Charname[20];
Intcount;
}leader[3]={“Zhang”,0,”Li”,0,”Wang”,0};
Main()
{
Inti,j;
Charleader_name[20];
For(i=1;i<=10;i++){
Scanf(“%s”,leader_name);
For(j=0;j<3;j++)
If(strcmp(leader_name,leader[j].name)==0)
Leader[j].count++;
}
Printf(“\n”);
For(i=0;i<3;i++)
Printf(“%5s:
%d\n”,leader[i].name,leader[i].count);
}
11.4结构体类型指针
一个结构体变量的指针就是该变量所占据的内存段的起始地址。
可以定义一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
11.4.1指向结构体变量的指针
1.指向一个结构体变量的指针定义形式:
Struct结构体名*指针变量名;
例如:
Structstudent
{
Longintnum;
Charname[20];
Charsex;
Floatscore;
};
Structstudent*pstu,stu;
Pstu=&stu;
2.由指向结构体变量的指针变量引用结构体成员的形式为:
(*指针变量).成员名或指针变量名->成员名
->为指向运算符
如:
上面示例用指针变量引用其成员的方式为:
(*pstu).num,(*pstu).name
(*pstu).sex,(*pstu).score
可以等价表示为:
Pstu->num,pstu->name,pstu->sex,pstu->score
例11.2比较结构体成员的几种引用方式
#include“stdio.h”
Main(){
Structstudent{
Longintnum;
Charname[20];
Charsex;
Floatscore;
};
Structstudentstu_1;
Structstudent*p;
P=&stu_1;
Stu_1.num=200601;
Strcpy(stu_1.name,”liping”);
Stu_1.sex=’M’;
Stu_1.score=811.5;
Printf(“No.:
%ld\nname:
%ssex:
%c\nscore:
%f\n”,stu_1.num,sut_1.name,stu_1.score);
Printf(“\nNo.:
%ld\nname:
%s\nsex:
%cscore:
%f\n”,(*p).num,(*p).name,(*p).sex,(*p).score);
Printf(“\nNo.:
%ld\nname:
%s\nsex:
%c\nscore:
%f\n”,p->num,p->name,p->sex,p->score);
}
11.4.2指向结构体数组元素的指针
一个指针变量可以指向一个结构体数组元素(将该结构体数组的数组元素地址赋给此指针变量)。
例如:
Struct
{
Inta;
Floatb;
}arr[3],*p;
P=arr;
此时使p指向arr数组的第一个元素,“p=arr;”等价于“p=&arr[0];”。
若执行“p++;”则此时指针变量p此时指向arr[1]。
例11.3输出数组中各元素中各成员的值。
Structstudent
{
Intnum;
Charname[20];
Charsex;
Intage;
};
Structstudentstu[3]={
{10101,”zhang”,’m’,18},
{10102,”li”,’m’,19},
{10103,”wang”,’f’,20}
};
Main(){
Structstudent*p;
Printf(“No.Namesexage\n”);
For(p=stu;pPrintf(“%5d%-20s%2c%4d\n”,p->num,p->name,p->sex,p->age);
}
注意:
(1)如果p的初值为stu,即指向第一个元素,则p+1后指向下一个元素。
请区别:
(++p)->num和(p++)->num
(2)指针p已定义为指向structstudent类型的数据,它只能指向该结构体类型数据,而不能指向一元素的某以成员(即p的地址不能是成员的地址)。
如下面的赋值是错误的:
p=&stu.num编译时将给出“警告”信息,表示地址的类型不匹配。
不要认为反正p是存放地址的,可以将任何地址赋给它。
11.5结构体与函数
11.5.1结构体变量作函数参数
将一个结构体变量的值传递给另一个函数,可以采用以下两种方式:
·用结构体变量的成员作参数。
用法和普通变量作实参是一样的,属“值传递”方式。
·形参与实参都用结构体变量
直接将实参结构体变量的各个成员之全部传递给形参的结构体变量。
注意:
实参和形参类型应当完全一致。
例11.4有一个结构体变量stu,内含学生学号、姓名和三门课的成绩。
要求在main函数中赋值,在另一函数print中将它们打印输出,程序如下。
Structstu
{
Intnum;
Charname[20];
Intscore[3];
};
Main(){
Voidprint(structstup);
Structstustu;intj;
Scanf(“%d”,&stu.num);
Scanf(“%s”,stu.name);
For(j=0;j<3;j++)
Scanf(“%d”,&stu.score[j]);
Print(stu);
}
Voidprint(structstup){
Intj;
Printf(“%d\n%s\n”,p.num,p.name);
For(j=0;j<3;j++)
Printf(“%d\n”,p.score[j]);
}
注意:
ANSIC允许用整个结构体作为函数的参数传递,但是必须保证实参与形参的类型相同。
·值得指出的是,把一个完整的结构体变量作为参数传递,虽然合法,但要将全部成员一个一个传递,既费时间又费空间,开销大,因此一般不采用。
11.5.2用指向结构体的指针作函数参数
用指向结构体变量(或数组)的起始地址传给形参。
形参为指针变量,实参为结构体变量的地址或指向结构体变量的指针。
例11.4的print函数形参改用指向结构体指针后程序如下:
#include“stdio.h”
Structstudent
{
Intnum;
Charname[20];
Intscore[3];
};
Main()
{voidprint(structstudent*p);
Structstudentstu;intj;
Scanf(“%d”,&stu.num);
Strcpy(stu.name,”liping”);
For(j=0;j<3;j++)
Scanf(“%d”,&stu.score[j]);
Print(&stu);
}
Voidprint(structstudent*p)
{
Intj;
Printf(“%d\n%s\n”,p->num,p->name);
For(j=0;j<3;j++)
Printf(“%d\n”,p->score[j]);
}
11.5.3返回结构体类型值的函数
ANSIC还允许函数返回结构体类型的值。
设有structstudent类型,结构体变量定义如下:
Structstudentstud;
若函数input()的功能是输入一个学生结构体数据,并将其返回给学生记录stud,即:
#include“stdio.h”
Structstudent
{intnum;
Charname[10];
Intscore[3];
};
Structstudentinput()
{
Intk;
Structstudentstud;
Scanf(“%d”,&stud.num);
Getchar();
Gets(stud.name);
For(k=0;k<3;k++)
Scanf(“%d”,&stud.score[k]);
Returnstud;
}
Main(){
Structstudentstud;
Intk;
Stud=input();
Pritnf(“%d%s”,stud.num,stud.name);
For(k=0;k<3;k++)
Pritnf(“%d”,stud.score[k]);
}
11.6链表
11.6.1链表概述
链表是一种常见的重要的数据结构,它是动态地进行存储分配的一种结构,根据需要开辟内存单元,使用链表可以减少内存空间的浪费。
所谓链表是指若干个结构体变量(每个结构体变量称为一个“结点”)按一定的原则连接起来。
每个结构体变量都包含有若干个数据和一个指向下一个结构体变量的指针,依靠这些指针将所有的结构体变量连接成一个链表。
例:
建立和输出一个简单的链表
Structstudent
{
Longnum;
Charname[20];
Structstudent*next;
};
Main(){
Structstudenta,b,c,*head,*p;
A.num=2002001;b.num=2002002;c.num=2002003;
Strcpy(a.name,”zhang”);
Strcpy(b.name,”sun”);
Strcpy(c.name,”li”);
Head=&a;
A.next=&b;
B.Next=&c;
C.Next=NULL;
P=head;
Do
{
Printf(“%ld,%s”,p->num,p->name);
P=p->next;
}while(p!
=NULL);
}
11.6.2内存管理库函数
链表结构是动态分配存储的,即在需要时才开辟一个结点的存储单元,怎样开辟呢?
C语言编译系统中提供了以下有关的函数:
1、分配存储空间函数malloc()
Malloc()函数的原型为:
Void*malloc(unsignedintsize);
函数的作用是在内存自由空间开辟一块大小为size字节的空间,并将此存储空间的起始地址作为函数值带回。
例如,malloc(10)的结果是分配了一个长度为10字节的内存空间,若系统设定的此内存空间的起始地址为1800,则malloc(10)的函数返回值就为1800.
例11.6malloc函数的使用
Main(){
Intj,n,*p;
Printf(“\npleaseinputadataton:
”);
Scanf(“%d”,&n);
P=(int*)malloc(n*sizeof(int));
Printf(“\npleaseinput%ddatas:
”,n);
For(j=0;jScanf(“%d”,p+j);
For(j=0;jPrintf(“%4d”,*(p+j));
}
2.calloc函数
函数原型为:
Void*calloc(unsignedintnum,unsignedintsize);
功能为:
分配num个size字节的空间。
3.重新分配空间函数realloc()
函数原型为:
Void*realloc(void*ptr,unsignedintsize);
函数用于使已分配的空间改变大小,即重新分配。
其作用是将ptr指向的存储区(是原先用malloc函数分配的)大小改变为size个字节,可以使原先的分配区扩大也可以缩小。
它的返回值是一个指针,即新的存储区的首地址。
4.释放空间函数free()
该函数的原型为:
Voidfree(void*ptr);
该函数的功能为:
将指针ptr指向的存储空间释放,交还给系统,系统可以另行分配作它用。
必须指出,ptr值不能是随意的地址,而是只能是程序在运行时通过动态申请分配到的存储空间的首地址。
下面的做法是正确的:
Pt=(int*)malloc(10);
……
……
Free(pt);
11.6.4链表的基本操作
链表的基本操作包括建立动态链表、链表的插入、删除、输出和查找等。
1.建立动态链表
所谓建立动态链表是指一个一个地输入各结点数据,并建立起各结点前后相链的关系。
建立动态链表有两种方式:
一种是在链表尾插入节点,一种是在链表头插入结点。
例:
写一个函数建立一个若干学生数据的单向动态链表。
(要求按学号从小到大的顺序建立)
#defineNULL0
Structstudent
{
Longnum;
Structstudent*next;
};
Structstudent*creat()
{
Structstudent*head,*p,*q;
Longx;
q=head=(structstudent*)malloc(sizeof(structstudent));
Head->next=NULL;
Printf(“\npleaseinputdatastothelist:
”);
Scanf(“%ld”,&x);
While(x!
=0){
P=(structstudent*)malloc(sizeof(structstudent));
P->num=x;
P->next=NULL;
q->next=p;
q=p;
Scanf(“%ld”,&x);
}
Return(head);
}
2.输出链表
Voidprint(structstudent*head)
{
Structstudent*p;
P=head->next;
Printf(“\nthelistis:
”);
If(p==NULL)
Printf(“thelistisNULL!
\n”);
While(p!
=NULL)
{
Printf(“%6ld”,p->num);
P=p->next;
}
Printf(“\n”);
}
3.删除链表中的一个结点
Voiddel(structstudent*head)
{
Structstudent*p,*q;
Longnum;
Printf(“\npleaseinputthestudent’numyouwanttodelete:
”);
Scanf(“%ld”,&num);
P=head->next;
While(num!
=p->num&&p->next!
=NULL){
Q=p;
P=p->next;
}
If(num==p->num)
{q->next=p->next;
Free(p);}else
Printf(“\n%ldisnotfound!
\n”,num);
}
4.在链表中插入一个结点
Voidinsert(structstudent*head)
{
Structstudent*p,*q,*t;
P=(structstudent*)malloc(sizeof(structstudent));
Printf(“\npleaseinputthestudentyouwanttoinsert:
”);
Scanf(“%ld”,&p->num);
Q=head