文章目录
C语言允许用户按自己的需要将相同或不同的基本类型构造成一种自定义
的特殊类型,即结构体和共用体。
结构体
结构体类型的定义
结构体的定义确定了如下两点:
- 定义结构体类型,确定结构中的成员项的名称及类型。
- 指明该结构体类型的变量在内存中的组织形式。
即定义结构体类型只规定了该结构体类型的内存分配模式,并没有开辟内存空间。只有定义了结构体变量或者结构体数组之后,才按照该结构体类型的内存分配模式开辟存储空间。
• 使用结构体类型必须经过两个步骤:
- 由关键字struct和用户定义的结构体名(共同称为结构体类型名)来定义类型。
- 再由该结构体类型来定义结构体变量名
结构体变量的声明
• 定义结构体只是确定该结构类型的名称及其成员项的组成及成员项的类
型。必须由定义的结构类型说明结构变量,才开辟相应的内存空间以供
使用。结构体变量有三种说明方式:
- 用已经定义的结构体类型名定义变量
- 在定义结构体类型的同时定义结构体变量
- 不定义结构体类型名,直接定义结构体变量
1 用已经定义的结构体类型名定义变量。
2. 在定义结构体类型的同时定义结构体变量
3.不定义结构体类型名,直接定义结构体变量
struct {
int id;
char name[31];
char gender;
char address[200];
double scores[3];
} alice, bob, clark;
说明:
- 注意类型和变量的区别。
- 成员项可单独使用,bob.id=140101
- 结构的成员项也可以是结构变量。
4.结构体类型的嵌套定义
struct Birthday{
int year;
int month;
int day;
};
struct Student{
int id;
char name[30];
struct Birthday birth;
char gender;
double scores[3];
};
结构体变量的初始化
结构体变量可以在说明时赋初值,称为初始化。可用花括号将这些结构类型变量对应的初值整体上括起来。
-
允许初值的个数少于结构体变量中成员的个数,但不允许初值的个数多于结构体变量中成员的个数。
-
在前者的情况下,执行初始化语句时就用初值表中的初值符对结构体变量中将排在前面的成员进行赋值,而余下的没有对应初值符的成员就被隐式初始化。 即:所有算术类型的成员被赋值0,所有指针类型的成员被赋予空指针常量
struct Birthday{
int year;
int month;
int day;
};
struct Student{
int id;
char name[30];
struct Birthday birth;
char gender;
double scores[3];
};
struct Student alice = {
200250,
"Alice",
{2002, 4},
'm',
{77, 88, 69.5}
};
struct Student alice = {
200250,
"Alice", {2002, 4},
'm', {77}};
那么,结构变量alice中成员alice.birth.day就隐含地被初始化为0 ,alice.scores[1]和alice.scores[2]也被初始化为0。
结构体变量的引用
- 引用结构体成员
• 由于结构体变量中各个成员的类型不同,一般情况下只能引用结构体变量的成员,不整体引用结构体变量。
• 无嵌套的情况下,引用结构体变量成员的方式为:
结构体变量名.成员项名
alice.id;
说明:
“.” 称为“结构体成员运算符”,这样引用的结构体成员相当于一个普通变量,与普通变量有相同的性质。
例如:
alice.id = 200250
作用:引用结构体变量alice中的成员id,相当于一个整型变量。
lucy.name
作用:结构体变量alice的成员name,相当于一个字符数组名。C语言某些库函数可通过首地址访问字符数组,例如:puts(lucy.name);
所以引用字符数组名是合法的。
lucy.score[0]
作用:引用结构体变量alice中的成员score[0],因为score是int型数组,不能整体引用,所以必须引用数组元素,相当于一个整型类型的变量。
- 结构体变量可以进行各种运算,也可以引用成员的地址或结构体变量的地址。
例如:scanf("%d",&alice.id);
- 结构体变量可以整体引用。
同一类型的结构体变量可以整体赋值。结构体变量内嵌的结构体类型成员情况也相同。
struct Student alice = {
200250,
"Alice",
{2002, 4},
'm',
{77}};
struct Student bob = alice;
bob.birth = alice.birth;
例题:定义一个结构体类型,成员项包括学生的学号、姓名、生日、性别、英语、数学、语文三门课程的分数,并定义该类型的结构体变量。编程实现结构体变量成员项的输入、输出。并求出三门课程的平均分
#include <stdio.h>
struct Birthday{
int year;
int month;
int day;
};
struct Student{
int id;
char name[30];
struct Birthday birth;
char gender;
double scores[3];
};
struct Student alice;
scanf("%d", &alice.id);
getchar();
gets(alice.name);
scanf("%d %d %d", &alice.birth.year, &alice.birth.month, &alice.birth.day);
getchar();
alice.gender = getchar();
for(int i = 0; i < 3; i ++){
scanf("%lf", &alice.scores[i]);
}
输出
printf("id: %d\nname: ", alice.id);
puts(alice.name);
printf("birthday: %d-%d-%d\n",
alice.birth.year,
alice.birth.month,
alice.birth.day);
printf("math\tenglish\tliterature\n");
for(int i = 0; i < 3; i++){
printf("%.1f\t", alice.scores[i]);
}
putchar('\n');
求三门课程的平均分
double average = 0;
for(int i = 0; i < 3; i ++){
average += alice.scores[i];
}
printf("%f", average);
结构体数组
结构体变量也可以构造成数组,称为结构体数组。每个数组元素都是一个结构变量,都含有结构成员项。它们在内存中的地址是连续的。
结构体数组的定义
struct 结构体类型名 结构体数组名 [(常量)表达式] ;
#include <stdio.h>
#define N 50
struct Birthday {
int year;
int month;
int day;
};
struct Student {
int id;
char name[30];
struct Birthday birth;
char gender;
double scores[3];
};
int main() {
struct Student students[N];
}
结构体数组的初始化
struct Student students[3] = {
{140101, "lucy", {1991, 1, 2}, 'M', {78, 89, 91}},
{140102, "lily", {1991, 2, 3}, 'F', {87, 60, 89}},
{140103, "tom", {1991, 3, 4}, 'M', {82, 91, 92}}
};
说明:
- 三个结构数组元素都含有struct Student 的所有成员项。
- 结构数组名students,代表结构数组的首地址。
结构体数组的使用
类似于一般数组的引用,对结构体数组的引用就是引用结构体数组元素。
- 除了初始化外,对结构体数组赋常数值、输入和输出、各种运算均是对结构体数组元素的成员进行的。
结构体数组元素的成员表示为:结构体数组名[下标].成员名
例如:students[0].id 和 scanf("%d", &students[0].id);
在嵌套的情况下为:
结构体数组名[下标].结构体成员名…结构体成员名. 基本成员名
- 结构体数组元素可以相互整体赋值。
例如:students[0] = students[1];
定义一个结构体,成员项包括学生的学号、姓名、生日、性别、英语、数学、政治三门课程的分数。
编程实现2个学生信息的输入、输出。求出每名同学的平均分,并排序。
#include <stdio.h>
#define N 2
struct Birthday {
int year;
int month;
int day;
};
struct Student {
int id;
char name[30];
struct Birthday birth;
char gender;
int scores[3];
double aver;
};
struct Student students[N];
for(int i = 0; i < N; i ++){
printf("请输入第%d位学生的信息\n", i + 1);
printf("请输入学号:");
scanf("%d", &students[i].id);
printf("请输入姓名:");
fflush(stdin);
gets(students[i].name);
printf("请输入出生年月日:");
scanf("%d %d %d", &students[i].birth.year, &students[i].birth.month, &students[i].birth.day);
printf("请输入性别:");
fflush(stdin);
students[i].gender = getchar();
printf("请分别输入三门课程的成绩:");
for(int j = 0; j < 3; j++){
scanf("%d", &students[i].scores[j]);
}
putchar('\n');
}
计算平均分
for(int i = 0; i < N; i ++){
students[i].aver = 0;
for(int j = 0; j < 3; j ++){
students[i].aver += students[i].scores[j];
}
students[i].aver /= 3.0;
}
按照平均分排序
for(int i = 0; i < N - 1; i ++){
for(int j = 0; j < N - 1 - i; j++){
if(students[j].aver < students[j + 1].aver){
struct Student tmp;
tmp = students[j];
students[j] = students[j + 1];
students[j + 1] = tmp;
}
}
}
printf("***** %d名学生信息如下 *****\n", N);
printf("学号\t%-10s\t出生年 月 日\t性别\t英语\t数学\t语文\t平均分\n","姓名");
for(int i = 0; i < N; i ++) {
printf("%d\t", students[i].id);
printf("%-10s\t", students[i].name);
printf("%6d%3d%3d\t", students[i].birth.year,
students[i].birth.month, students[i].birth.day);
if (students[i].gender == 'M') printf("男\t");
else if (students[i].gender == 'F') printf("女\t");
else printf("无\t");
for (int j = 0; j < 3; j++) {
printf("%d\t", students[i].scores[j]);
}
printf("%6.2f\n", students[i].aver);
}
结构体指针
指向结构体变量的指针是结构体变量所占内存单元的首地址
指向结构体变量的指针变量
struct Student alice;
struct Student *ptr = &alice;
说明:
1. ptr 即为指向结构体变量 alice 的指针变量。
2. 结构体指针变量加1,地址加1个结构体变量所占的字节数。
用指向结构体变量的指针变量表示结构体变量的成员。格式:
• 方式一:(*p).成员项名
• 方式二: p->成员项名
• 例:如果定义了指向结构体变量 alice 的指针变量 ptr,*ptr 表示
ptr 所指向的结构体变量 alice,其成员 alice.id 可表示为:
(*ptr).id 或者 ptr->id
• 两种表示法等价,运算符“->”与“.”运算符优先级相同,具有最高的
优先级
例题:定义一个结构体,成员项包括学生的学号、姓名、生日、性别、英语、数学、政治三门课程的分数,编程实现结构体变量成员项的输出,用指针实现。
struct Student alice = {200250, "Alice", {2002, 2, 3}, 'M', {78, 89, 75}};
struct Student *ptr = &alice;
printf("%d\n", ptr->id);
puts(ptr->name);
printf("%d-%d-%d\n", ptr->birth.year,ptr->birth.month, ptr->birth.day);
printf("%c\n", ptr->gender);
for(int i = 0; i < 3; i ++){
printf("%5d", ptr->scores[i]);
}
结构体数组与结构体指针变量
一维数组的数组名代表数组的首地址,一维结构体数组也一样。
可以将一维结构体数组名赋给指向结构体的指针变量,该指针变量将指向下标
为0的元素,它可以在数组元素之间移动
例题:定义一个结构体,成员项包括学生的学号、姓名、出生日期、性别、英语、数学、语文三门课程的分数,编程实现N个学生,每个学生的平均成绩,然后输出。
struct Student students[N] = {
{140101, "Alice", {1991, 1, 2}, 'M', {78, 89, 91}},
{140102, "Bob", {1991, 2, 3}, 'F', {87, 60, 89}},
{140103, "Clark", {1991, 3, 4}, 'M', {82, 91, 92}}
};
struct Student *p;
for(p = students; p < students + N; p++){
p->aver = 0;
for(int j = 0; j < 3; j ++){
p->aver += p->scores[j];
}
p->aver /= 3;
printf("%s's average score is %f\n", p->name, p->aver);
}
结构体与函数
匿名结构
struct names{
char first[20];
char last[20];
};
嵌套结构成员
struct person
{
int id;
struct names name;
};
- 匿名结构
struct person
{
int id;
struct {char first[20]; char last[20];};
}
两者初始化方式相同:
struct person jack = {1000, {“Jack”, “Zhang”}};
两者访问方式不同:
jack.name.first
jack.first 省略了 name