一、定义和使用结构体变量
1、自己建立结构体类型
C 语言允许用户自己定义由不同类型数据组成的组合型的数据结构,它称为 结构体
一般形式:
struct 结构体名{
成员表列;
}
结构体类型的名字是由一个关键字 struct 和结构体名二者组合而成的
结构体名由用户指定,又称 “结构体标记”,以区别于其他的结构体类型
大括号内是该结构体所包括的子项,称为结构体的成员,对各成员都应进行类型声明
类型名 成员名;
"成员表列" 也称为 “域表”,每一个成员是结构体的一个域
成员名命名规则与变量名相同
成员可以是另一个结构体变量
2、定义结构体类型变量
1)先声明结构体类型,再定义该类型的变量
在定义了结构体变量后,系统会为之分配内存单元
2)在声明类型的同时定义变量
struct 结构体名{
成员表列;
}变量名表列;
3)不指定类型名而直接定义结构体类型变量
struct{
成员表列;
}变量名表列;
- 结构体类型与结构体变量是不同的。只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间
- 结构体类型中的成员可以与程序中的变量名相同,但二者不代表同一对象
- 对结构体变量中的成员,可以单独使用,它的作用与地位相当于普通变量
3、结构体变量的初始化和引用
把一个学生的信息放在一个结构体变量中,然后输出这个学生的信息
#include <stdio.h>
int main(){
struct student{
int num;
char name[20];
char sex;
char addr[20];
} student1 = {10101,"Li Lin",'M',"123 BeiJing Road"};
printf("No.:%d\nname:%s\nsex:%c\naddress:%s\n", student1.num, student1.name, student1.sex, student1.addr);
return 0;
}
引用结构体变量应遵循以下规则:
- 可以引用结构体变量中成员的值,引用方式:
-
结构体变量名.成员名
- 不能通过结构体变量名来得到结构体变量中所有成员的值
-
- 如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级的找到最低一级的成员
- 对结构体变量的成员可以像普通变量一样进行各种运算(“ . ”运算符的优先级最高)
- 同类的结构体变量可以互相赋值
- 可以引用结构体变量成员的地址,也可以引用结构体变量的地址
// 正确用法
// 输入 student.num 的值
scanf("%d",&student1.num);
// 输出结构体变量 student1 的首地址
printf("%o",&student1);
// 错误用法
scanf("%d,%s,%c,%d,%f,%s",&studen1);
二、结构体数组
每一个数组元素都是一个结构体类型的数据的数组称为 结构体数组
有 3 个候选人,每个选民只能投票选一人,要求编写一个统计选票的程序,先后输入被选人的名字,最后输出各人得票结果
#include <string.h>
#include <stdio.h>
struct person{
char name[20];
int count;
} leader[3] = {"Li",0,"Zhang",0,"Fun",0};
int main(){
int i,j;
char leader_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("\nResult:\n");
for(i = 0;i < 3;i++){
printf("%5s:%d\n",leader[i].name,leader[i].count);
}
return 0;
}
定义结构体数组一般形式
{成员表列} 数组名[数组长度];
结构体类型 数组名[数组长度];
struct person leader[3];
对结构体数组初始化的形式是在定义数组的后面加上
= {初值表列};
三、结构体指针
结构体指针,指向结构体数据的指针,一个结构体变量的起始地址就是这个结构体变量的指针
指针变量既可以指向结构体变量,也可以用来指向结构体数组中的元素。但是指针变量的基类型必须与结构体变量的类型相同
通过指向结构体变量的指针变量输出结构体变量中成员的信息
#include <stdio.h>
#include <string.h>
int main(){
struct student{
long num;
char name[20];
char sex;
float score;
}; // 逗号不能漏掉!!!!!!!!!!!!
struct student stu_1;
struct student *p;
p = &stu_1;
stu_1.num = 10101;
strcpy(stu_1.name,"Li Lin");
stu_1.sex = 'M';
stu_1.score = 89.5;
printf("No.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n",stu_1.num,stu_1.name,stu_1.sex,stu_1.score);
// *p = stu_1
printf("\nNo.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n",(*p).num,(*p).name,(*p).sex,(*p).score);
return 0;
}
为了使用方便和使之直观,C 语言允许直接把 p->num 代替 (*p).num ,它表示 p 所指向的结构体变量中的 num 成员
如果 p 指向一个结构体变量,以下 3 种形式等价
- 结构体变量.成员名
- (*p).成员名
- p -> 成员名
有 3 个学生的信息,放在结构体数组中,要求输出全部学生的信息
#include <stdio.h>
struct student{
int num;
char name[20];
char sex;
int age;
};
struct student stu[3] = {
{10101,"Li Lin",'M',18},
{10102,"Zhang Fun",'M',19},
{10103,"Wang Min",'F',18}
};
int main(){
struct student *p;
printf("No. Name sex age\n");
for(p = stu;p < stu + 3;p++){
printf("%-23d %-21s %3c %24d\n",p->num,p->name,p->sex,p->age);
}
return 0;
}
运行结果:
No. Name sex age
10101 Li Lin M 18
10102 Zhang Fun M 19
10103 Wang Min F 18
如果 p 的初值为 stu,即 p 指向 stu 的第一个元素,p 加 1 后,p 就指向下一个元素
p 是一个指向 struct student 类型数据的指针变量,它用来指向一个 struct student 类型的数据的起始地址,不应用来指向 stu 数组元素中的某一成员
四、用结构体变量和结构体变量的指针作函数参数
将一个结构体变量的值传递给一个函数,有 3 种方法
- 用结构体变量的成员作参数
- 属于值传递
- 应注意实参与形参的类型保持一致
- 用结构体变量作参数
- 属于值传递
- 用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参
有 N 个结构体变量 stu,内含学号、姓名和 3 门课程的成绩。要求输出平均成绩最高的学生的信息(包括学号、姓名、3 门课程成绩和平均成绩)
#include <stdio.h>
#define N 3
struct student{
int num;
char name[20];
float score[3];
float aver;
};
int main(){
void input(struct student stu[]);
struct student max(struct student stu[]);
void print(struct student stu);
struct student stu[N],*p = stu;
input(p);
print(max(p));
return 0;
}
void input(struct student stu[]){
int i;
printf("请输入各学生的信息:学好、姓名、三门课程成绩!\n");
for(i = 0;i < N;i++){
scanf("%d %s %f %f %f", &stu[i].num,stu[i].name,&stu[i].score[0], &stu[i].score[1],&stu[i].score[2]);
stu[i].aver = (stu[i].score[0] + stu[i].score[1] + stu[i].score[2]) / 3.0;
}
}
struct student max(struct student stu[]){
int i,m = 0;
for(i = 0;i < N;i++){
if(stu[i].aver > stu[m].aver){
m = i;
}
}
return stu[m];
}
void print(struct student stud){
printf("\n成绩最高的学生是:\n");
printf("学好:%d\n姓名:%s\n三门课程成绩:%5.1f,%5.1f,%5.1f\n平均成绩:%6.2f\n",stud.num,stud.name,stud.score[0],stud.score[1],stud.score[2],stud.aver);
}
运行结果:
请输入各学生的信息:学好、姓名、三门课程成绩!
10101 LiLin 78 89 98
10102 WangFun 98.5 87 69
10103 WeiMin 88 76.5 89
成绩最高的学生是:
学好:10101
姓名:LiLin
三门课程成绩: 78.0, 89.0, 98.0
平均成绩: 88.33
五、用指针处理链表
1、什么是线性链表
静态的数据结构:
分配固定的存储单元,在内存中连续存放(如数组)
动态的数据结构:
没有固定大小,根据需要随时开辟存储单元,用完后可以随时释放存储单元(线性链表)
链表有一个 “头指针” 的变量,存放一个地址,该地址指向链表中的第一个元素
链表中的各元素称为 “结点”,每个结点都应包括两个部分:
- 用户需要用的实际数据
- 下一个结点的地址
链表中各结点在内存中的地址可以是不连续的
链表结构体类型:
struct student{
int num;
float score;
struct student *next;
}
2、建立简单的静态链表
所有结点都是成程序中定义的,不是临时开辟的,也不能用完后释放,这种链表称为“ 静态链表 ”
建立如图所示的简单链表,由 3 个学生数据的结点组成。输出各结点中的数据
#include <stdio.h>
#define NULL 0
struct student{
int num;
float score;
struct student *next;
};
int main(){
struct student a,b,c,*head,*p;
a.num = 10101;
a.score = 89.5;
b.num = 10102;
b.score = 90;
c.num = 10103;
c.score = 85;
head = &a;
a.next = &b;
b.next = &c;
c.next = NULL;
p = head;
do{
printf("%ld %5.1f\n",p -> num,p -> score);
p = p -> next;
} while(p != NULL);
return 0;
}
3、建立动态链表
所谓动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相连的关系
建立一个有两名学生学号和成绩数据的单向动态链表
#include <stdio.h>
#include <malloc.h>
#define LEN sizeof(struct student)
struct student{
int num;
float score;
struct student *next;
};
int main(){
struct student *head,*p;
head = p = (struct student *) malloc(LEN);
scanf("%d %f",&p -> num,&p -> score);
p = (struct student *)malloc(LEN);
scanf("%d %f",&p -> num,&p -> score);
head -> next = p;
p -> next = NULL;
p = head;
printf("\n结点1:%d,%6.2f\n",p -> num,p -> score);
p = p -> next;
printf("结点2:%d,%6.2f\n",p -> num,p -> score);
return 0;
}
六、提高
1、共用体类型
几个不同的变量共占同一段内存的结构,称为 “ 共用体 ” 类型的结构
一般形式:
union 共用体名{
成员表列;
} 变量表列;
结构体变量所占内存长度是各成员占的内存长度之和,每个成员分别占有其自己的内存单元
共用体变量所占内存长度等于最长的成员的长度
不用引用共用体变量,只能引用共用体变量中的成员
2、枚举类型
如果一个变量只有几种可能的值,则可以定义为 枚举类型
“枚举” 就是将变量值一一列举出来,变量的值只限于列举出来的值的范围内
声明枚举类型:
enum weekday{
sun,mon,tue,wed,thu,fri,sat
};
定义枚举变量:
enum weekday workday,week_end;
workday,week_end 即为枚举变量,其值在 sun 到 sat 之间的一个
其中 sun …… sat 称为 枚举元素 或 枚举常量
口袋中有 红、黄、蓝、白、黑 5 种颜色的球若干个。每次从口袋中先后取出 3个球,问得到 3 种不同的的球的可能排列,输出每种排列的情况
#include <stdio.h>
int main(){
enum color{
red,
yellow,
blue,
white,
black
};
enum color i,j,k,pri;
int n,loop;
n = 0;
for(i = red;i <= black;i++){
for(j = red;j <= black;j++){
if(i != j){
for(k = red;k <= black;k++){
if((k != i) && (k != j)){
n = n + 1;
printf("%-4d",n);
for(loop = 1;loop <= 3;loop++){
switch(loop){
case 1:
pri = i;
break;
case 2:
pri = j;
break;
case 3:
pri = k;
break;
default:
break;
} // switch(loop) END
switch(pri){
case red:
printf("%-10s","red");
break;
case yellow:
printf("%-10s","yellow");
break;
case blue:
printf("%-10s","blue");
break;
case white:
printf("%-10s","white");
break;
case black:
printf("%-10s","black");
break;
default:
break;
} // switch(pri) END
} // for(loop = 1;loop <=3;loop++) END
printf("\n");
} // if((k != i) && (k != j)) END
} // for(k = red;k <= black;k++) END
} // if(i != j) END
} // for(j = red;j <= black;j++) END
} // for(i = red;i <= black;i++) END
printf("\ntotal : %5d\n",n);
return 0;
} // int main() END
运行结果
1 red yellow blue
2 red yellow white
3 red yellow black
…… …… …… ……
58 black white red
59 black white yellow
60 black white blue
total : 60
一 叶 知 秋,奥 妙 玄 心