📄 结构与联合.01
字号:
gets(man[i].phone);
}
printf("name\t\t\tphone\n\n");
for(i=0;i<NUM;i++)
printf("%s\t\t\t%s\n",man[i].name,man[i].phone);
}
本程序中定义了一个结构mem,它有两个成员name和phone 用来
表示姓名和电话号码。在主函数中定义man为具有mem 类型的结构数
组。在for语句中,用gets函数分别输入各个元素中两个成员的值。
然后又在for语句中用printf语句输出各元素中两个成员值。结构指针变量的说明和使用
一个指针变量当用来指向一个结构变量时, 称之为结构指针变
量。结构指针变量中的值是所指向的结构变量的首地址。 通过结构
指针即可访问该结构变量, 这与数组指针和函数指针的情况是相同
的。结构指针变量说明的一般形式为: struct 结构名*结构指针变量名 例如,在前面的例7.1中定义了stu这个结构, 如要说明一个指
向stu的指针变量pstu,可写为: struct stu *pstu; 当然也可在定义stu结构时同时说明pstu。与前面讨论的各类指
针变量相同,结构指针变量也必须要先赋值后才能使用。
赋值是把结构变量的首地址赋予该指针变量, 不能把结构名赋
予该指针变量。如果boy是被说明为stu类型的结构变量,则: pstu=&boy是正确的,而: pstu=&stu是错误的。
结构名和结构变量是两个不同的概念,不能混淆。 结构名只能
表示一个结构形式,编译系统并不对它分配内存空间。 只有当某变
量被说明为这种类型的结构时,才对该变量分配存储空间。 因此上
面&stu这种写法是错误的,不可能去取一个结构名的首地址。 有了
结构指针变量,就能更方便地访问结构变量的各个成员。
其访问的一般形式为: (*结构指针变量).成员名 或为:
结构指针变量->成员名 例如: (*pstu).num或者: pstu->num
应该注意(*pstu)两侧的括号不可少, 因为成员符“.”的优先
级高于“*”。如去掉括号写作*pstu.num则等效于*(pstu.num),这
样,意义就完全不对了。 下面通过例子来说明结构指针变量的具体说明和使用方法。[例7.6] struct stu
{
int num;
char *name;
char sex;
float score;
} boy1={102,"Zhang ping",'M',78.5},*pstu;
main()
{
pstu=&boy1;
printf("Number=%d\nName=%s\n",boy1.num,boy1.name);
printf("Sex=%c\nScore=%f\n\n",boy1.sex,boy1.score);
printf("Number=%d\nName=%s\n",(*pstu).num,(*pstu).name);
printf("Sex=%c\nScore=%f\n\n",(*pstu).sex,(*pstu).score);
printf("Number=%d\nName=%s\n",pstu->num,pstu->name);
printf("Sex=%c\nScore=%f\n\n",pstu->sex,pstu->score);
}
本例程序定义了一个结构stu,定义了stu类型结构变量boy1 并
作了初始化赋值,还定义了一个指向stu类型结构的指针变量pstu。
在main函数中,pstu被赋予boy1的地址,因此pstu指向boy1 。
然后在printf语句内用三种形式输出boy1的各个成员值。 从运行结
果可以看出: 结构变量.成员名
(*结构指针变量).成员名
结构指针变量->成员名 这三种用于表示结构成员的形式是完全等效的。结构数组指针变量
结构指针变量可以指向一个结构数组, 这时结构指针变量的值
是整个结构数组的首地址。 结构指针变量也可指向结构数组的一个
元素,这时结构指针变量的值是该结构数组元素的首地址。
设ps为指向结构数组的指针变量,则ps也指向该结构数组的0号
元素,ps+1指向1号元素,ps+i则指向i号元素。 这与普通数组的情
况是一致的。[例7.7]用指针变量输出结构数组。struct stu
{
int num;
char *name;
char sex;
float score;
}boy[5]={
{101,"Zhou ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"Liou fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
main()
{
struct stu *ps;
printf("No\tName\t\t\tSex\tScore\t\n");
for(ps=boy;ps<boy+5;ps++)
printf("%d\t%s\t\t%c\t%f\t\n",ps->num,ps->name,ps->sex,ps->
score);
}
在程序中,定义了stu结构类型的外部数组boy 并作了初始化赋
值。在main函数内定义ps为指向stu类型的指针。在循环语句for 的
表达式1中,ps被赋予boy的首地址,然后循环5次,输出boy 数组中
各成员值。 应该注意的是, 一个结构指针变量虽然可以用来访问结构变量
或结构数组元素的成员,但是,不能使它指向一个成员。 也就是说
不允许取一个成员的地址来赋予它。因此,下面的赋值是错误的。 ps=&boy[1].sex;而只能是:
ps=boy;(赋予数组首地址)
或者是:
ps=&boy[0];(赋予0号元素首地址)结构指针变量作函数参数
在ANSI C标准中允许用结构变量作函数参数进行整体传送。 但
是这种传送要将全部成员逐个传送, 特别是成员为数组时将会使传
送的时间和空间开销很大,严重地降低了程序的效率。 因此最好的
办法就是使用指针,即用指针变量作函数参数进行传送。 这时由实
参传向形参的只是地址,从而减少了时间和空间的开销。[例7.8]题目与例7.4相同,计算一组学生的平均成绩和不及格人数。
用结构指针变量作函数参数编程。struct stu
{
int num;
char *name;
char sex;
float score;}boy[5]={
{101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
main()
{
struct stu *ps;
void ave(struct stu *ps);
ps=boy;
ave(ps);
}
void ave(struct stu *ps)
{
int c=0,i;
float ave,s=0;
for(i=0;i<5;i++,ps++)
{
s+=ps->score;
if(ps->score<60) c+=1;
}
printf("s=%f\n",s);
ave=s/5;
printf("average=%f\ncount=%d\n",ave,c);
}
本程序中定义了函数ave,其形参为结构指针变量ps。boy 被定
义为外部结构数组,因此在整个源程序中有效。在main 函数中定义
说明了结构指针变量ps,并把boy的首地址赋予它,使ps指向boy 数
组。然后以ps作实参调用函数ave。在函数ave 中完成计算平均成绩
和统计不及格人数的工作并输出结果。
与例7.4程序相比,由于本程序全部采用指针变量作运算和处理,
故速度更快,程序效率更高。.topoic=动态存储分配
在数组一章中,曾介绍过数组的长度是预先定义好的, 在整个
程序中固定不变。C语言中不允许动态数组类型。例如: int n;
scanf("%d",&n);
int a[n]; 用变量表示长度,想对数组的大小作动态说明, 这是错误的。
但是在实际的编程中,往往会发生这种情况, 即所需的内存空间取
决于实际输入的数据,而无法预先确定。对于这种问题, 用数组的
办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,
这些内存管理函数可以按需要动态地分配内存空间, 也可把不再使
用的空间回收待用,为有效地利用内存资源提供了手段。 常用的内
存管理函数有以下三个:
1.分配内存空间函数malloc调用形式: (类型说明符*) malloc (size) 功能:在内存的动态存储区中分配一块长度为"size" 字节的连续区
域。函数的返回值为该区域的首地址。 “类型说明符”表示
把该区域用于何种数据类型。(类型说明符*)表示把返回值强
制转换为该类型指针。“size”是一个无符号数。例如: pc=(char *) malloc (100); 表示分配100个字节的内存空间,并强制转换为字符数组类型,
函数的返回值为指向该字符数组的指针, 把该指针赋予指针
变量pc。
2.分配内存空间函数 calloc
calloc 也用于分配内存空间。调用形式: (类型说明符*)calloc(n,size) 功能:在内存动态存储区中分配n块长度为“size”字节的连续区域。
函数的返回值为该区域的首地址。(类型说明符*)用于强制类
型转换。calloc函数与malloc 函数的区别仅在于一次可以分
配n块区域。例如: ps=(struet stu*) calloc(2,sizeof (struct stu)); 其中的sizeof(struct stu)是求stu的结构长度。因此该语句
的意思是:按stu的长度分配2块连续区域,强制转换为stu类
型,并把其首地址赋予指针变量ps。
3.释放内存空间函数free调用形式: free(void*ptr); 功能:释放ptr所指向的一块内存空间,ptr 是一个任意类型的指针
变量,它指向被释放区域的首地址。被释放区应是由malloc或
calloc函数所分配的区域:[例7.9]分配一块区域,输入一个学生数据。main()
{
struct stu
{
int num;
char *name;
char sex;
float score;
} *ps;
ps=(struct stu*)malloc(sizeof(struct stu));
ps->num=102;
ps->name="Zhang ping";
ps->sex='M';
ps->score=62.5;
printf("Number=%d\nName=%s\n",ps->num,ps->name);
printf("Sex=%c\nScore=%f\n",ps->sex,ps->score);
free(ps);
}
本例中,定义了结构stu,定义了stu类型指针变量ps。 然后分
配一块stu大内存区,并把首地址赋予ps,使ps指向该区域。再以ps
为指向结构的指针变量对各成员赋值,并用printf 输出各成员值。
最后用free函数释放ps指向的内存空间。 整个程序包含了申请内存
空间、使用内存空间、释放内存空间三个步骤, 实现存储空间的动
态分配。链表的概念
在例7.9中采用了动态分配的办法为一个结构分配内存空间。每
一次分配一块空间可用来存放一个学生的数据, 我们可称之为一个
结点。有多少个学生就应该申请分配多少块内存空间, 也就是说要
建立多少个结点。当然用结构数组也可以完成上述工作, 但如果预
先不能准确把握学生人数,也就无法确定数组大小。 而且当学生留
级、退学之后也不能把该元素占用的空间从数组中释放出来。 用动
态存储的方法可以很好地解决这些问题。 有一个学生就分配一个结
点,无须预先确定学生的准确人数,某学生退学, 可删去该结点,
并释放该结点占用的存储空间。从而节约了宝贵的内存资源。 另一
方面,用数组的方法必须占用一块连续的内存区域。 而使用动态分
配时,每个结点之间可以是不连续的(结点内是连续的)。 结点之间
的联系可以用指针实现。 即在结点结构中定义一个成员项用来存放
下一结点的首地址,这个用于存放地址的成员,常把它称为指针域。
可在第一个结点的指针域内存入第二个结点的首地址, 在第二
个结点的指针域内又存放第三个结点的首地址, 如此串连下去直到
最后一个结点。最后一个结点因无后续结点连接,其指针域可赋为0。
这样一种连接方式,在数据结构中称为“链表”。图7.3为链表的示
意图。
在图7.3中,第0个结点称为头结点, 它存放有第一个结点的首
地址,它没有数据,只是一个指针变量。 以下的每个结点都分为两
个域,一个是数据域,存放各种实际的数据,如学号num,姓名name,
性别sex和成绩score等。另一个域为指针域, 存放下一结点的首地
址。链表中的每一个结点都是同一种结构类型。例如, 一个存放学
生学号和成绩的结点应为以下结构:
struct stu
{ int num;
int score;
struct stu *next;
} 前两个成员项组成数据域,后一个成员项next构成指针域, 它
是一个指向stu类型结构的指针变量。链表的基本操作
对链表的主要操作有以下几种: 1.建立链表;
2.结构的查找与输出;
3.插入一个结点;
4.删除一个结点;下面通过例题来说明这些操作。[例7.10]建立一个三个结点的链表,存放学生数据。
为简单起见, 我们假定学生数据结构中只有学号和年龄两项。
可编写一个建立链表的函数creat。程序如下:
#define NULL 0
#define TYPE struct stu
#define LEN sizeof (struct stu)
struct stu
{
int num;
int age;
struct stu *next;
};
TYPE *creat(int n)
{
struct stu *head,*pf,*pb;
int i;
for(i=0;i<n;i++)
{
pb=(TYPE*) malloc(LEN);
printf("input Number and Age\n");
scanf("%d%d",&pb->num,&pb->age);
if(i==0)
pf=head=pb;
else pf->next=pb;
pb->next=NULL;
pf=pb;
}
return(head);
}
在函数外首先用宏定义对三个符号常量作了定义。这里用 TYPE
表示struct stu,用LEN表示sizeof(struct stu)主要的目的是为了
在以下程序内减少书写并使阅读更加方便。结构stu定义为外部类型,
程序中的各个函数均可使用该定义。
creat函数用于建立一个有n个结点的链表,它是一个指针函数,
它返回的指针指向stu结构。在creat函数内定义了三个stu结构的指
针变量。head为头指针,pf 为指向两相邻结点的前一结点的指针变
量。pb为后一结点的指针变量。在for语句内,用malloc函数
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -