第8章 结 构 体
目 录 结构体 结构体数组 指向结构体的指针 定义类型别名 动态存储分配函数 结构体的应用—链表 C语言程序设计 - 第8章 结构体
结构体 (Structure) 结构体概述 结构体类型的声明 结构体变量的定义 结构体变量的初始化 结构体变量的引用
结构体概述 结构体 举例 将不同类型的数据组合成一个整体 用来表示简单类型无法描述的复杂对象 可以用结构体来定义用户自己的数据结构 描述学生信息 num name sex age score addr 12039 Bill Gates M 40 76.5 New York C语言程序设计 - 第8章 结构体
结构体类型的声明 一般形式 struct [结构体名] { 成员表列 }; “成员表列”形式 类型 成员名; ... ...
结构体类型声明的说明 (1) 声明了一种类型,而不是定义变量 结构体名可以没有,但是这样就无法再次使用该结构体类型了 成员表列中是成员(Member)的定义 成员的定义形式与变量定义相同 成员类型可以是另一结构体类型,但不可直接或间接递归嵌套 成员表列不可为空,至少要有一个成员 C语言程序设计 - 第8章 结构体
结构体类型声明的说明 (2) 注意{}不表示复合语句,其后有分号 同一结构体的成员不能重名 不同结构体的成员可以重名 结构体成员和其他变量可以重名 结构体类型与其成员或其他变量可重名 struct test { int test; } test; 结构体类型名称是struct 结构体名,注意struct关键字不能省略 C语言程序设计 - 第8章 结构体
结构体类型声明的说明 (3) 即使两个结构体声明中的成员类型、名称、顺序都完全一致,它们也是不同的类型 结构体类型也要“先声明,后使用” 如果结构体类型声明在函数内部,则该函数之外无法引用此结构体类型 一般把结构体类型声明放到文件最前面 也可以把结构体类型声明放在头文件里 C语言程序设计 - 第8章 结构体
结构体类型的声明举例 struct student { unsigned num; /* 学号 */ char name[20]; /* 姓名 */ char sex; /* 性别 */ unsigned age; /* 年龄 */ float score; /* 分数 */ char addr[50]; /* 地址 */ }; C语言程序设计 - 第8章 结构体
结构体变量的定义 (1) 先声明结构体类型再定义变量 struct student { unsigned num; char name[20]; char sex; unsigned age; float score; char addr[50]; }; struct student stu1, stu2; C语言程序设计 - 第8章 结构体
结构体变量的定义 (2) 在声明结构体类型的同时定义变量 struct student { unsigned num; char name[20]; char sex; unsigned age; float score; char addr[50]; } stu1, stu2; C语言程序设计 - 第8章 结构体
结构体变量的定义 (3) 直接定义无名结构类型变量 struct { unsigned num; char name[20]; char sex; unsigned age; float score; char addr[50]; } stu1, stu2; C语言程序设计 - 第8章 结构体
结构体声明和变量定义举例 struct date { int year, month, day; }; struct student{ unsigned num; char name[20]; char sex; struct date birthday; float score; } stu1, stu2; C语言程序设计 - 第8章 结构体
结构体变量的初始化 按照成员的顺序和类型对成员初始化 struct date date1 = {1984, 10, 20}; struct student stu = { 1001, /*unsigned num*/ "Tom", /*char name[20]*/ 'M', /*char sex*/ {1983, 9, 20},/*struct date birthday*/ 93.5 /*float score*/ }; C语言程序设计 - 第8章 结构体
结构体变量中成员的引用 一般形式 成员运算符 . 说明 结构体变量名.成员名 具有最高的优先级,自左向右结合 结构体成员和同类型的变量用法相同 若成员类型又是一个结构体,则可以使用若干个成员运算符,访问最低一级的成员 C语言程序设计 - 第8章 结构体
结构体变量中成员的引用举例 struct student stu; ... ... scanf("%f", &stu.score); stu.num = 12345; stu.birthday.month = 11; stu.score = sqrt(stu.score) * 10; strcpy(stu.name, "Mike"); printf("No.%d:", stu.num); C语言程序设计 - 第8章 结构体
结构体变量整体引用 结构体类型变量之间可以直接相互赋值 函数的实参和形参可以是结构体类型,并且遵循实参到形参的单向值传递规则 实质上是两个结构体变量相应的存储空间中的所有数据直接拷贝 包括复杂类型在内的所有结构体成员都被直接赋值,如字符串、结构体类型等 函数的实参和形参可以是结构体类型,并且遵循实参到形参的单向值传递规则 为了提高程序的效率,函数的参数多使用结构体类型指针 C语言程序设计 - 第8章 结构体
结构体变量整体引用举例 struct student stu1, stu2={1002, "Kate", 'F', {1981, 11, 4}, 89.0}; void print(struct student s) { printf("%d,%4s,%c,%d.%02d.%02d,%4.1f\n", s.num, s.name, s.sex, s.birthday.year, s.birthday.month, s.birthday.day, s.score); } void main () stu1 = stu2; /* 直接赋值 */ print(stu1); /* 1002,Kate,F,1981.11.04,89.0 */ C语言程序设计 - 第8章 结构体
结构体数组 结构体数组的用法与基本类型数组类似 结构体数组可用于表示二维表格 举例 定义、初始化、引用等 struct student s[10]; for (i=0; i<10; i++) scanf("%d,%s,%c,%d,%f", &s[i].num, s[i].name, &s[i].sex, &s[i].age, &s[i].score); C语言程序设计 - 第8章 结构体
结构体数组初始化及应用举例 struct student stu[] = { {1001,"Tom",'M',{1980,1,2},85.5}, {1002,"Kate",'F',{1981,11,4},89.0}, {1003,"Mike",'M',{1980,3,5},95.5}}; for (i=0; i<3; i++) printf("%d,%4s,%c,%d.%02d.%02d,%4.1f\n", stu[i].num, stu[i].name, stu[i].sex, stu[i].birthday.year, stu[i].birthday.month, stu[i].birthday.day, stu[i].score); C语言程序设计 - 第8章 结构体
结构体数组与二维表 struct student s[5]; 结构体 num name sex birthday score year month day s[0] 1001 Tom M 1980 1 2 85.5 s[1] 1002 Kate F 1981 11 4 89.0 s[2] 1003 Mike 3 5 95.5 s[3] 1004 John 8 13 73.0 s[4] 1005 Lily 6 81.0 一维数组 C语言程序设计 - 第8章 结构体
指向结构体的指针 指向结构体的指针 指向运算符 -> 定义、使用与其他基本类型指针类似 可以使用指向运算符(->)引用指针所指向的结构体的成员 指向运算符 -> 结构体指针->成员名 具有最高的优先级,自左向右结合 若struct student stu, *p=&stu; 则stu.num、(*p).num、p->num等效 C语言程序设计 - 第8章 结构体
指向结构体数组的指针 指向结构体数组的指针 举例 与指向其他基本类型数组的指针用法类似 注意相关运算符的结合方向和优先级 struct student stu[10], p=stu; ++p->num; /* 同++(p->num); */ p++->num; /* 同(p++)->num; */ (++p)->num; (p++)->num; C语言程序设计 - 第8章 结构体
结构体指针作函数参数举例 void input(struct student *p) { scanf("%d %s %c %d.%d.%d %f", &p->num, p->name, &p->sex, &p->birthday.year, &p->birthday.month, &p->birthday.day, &p->score); } void main () struct student stu[20]; int i; for (i=0; i<20; i++) input(stu+i); C语言程序设计 - 第8章 结构体
定义类型别名 一般形式 举例 typedef <变量定义形式中变量名换成类型名> typedef float REAL; typedef struct { int month; int day; int year; } DATE; C语言程序设计 - 第8章 结构体
动态存储分配函数 动态分配存储 相关函数 说明 根据需要开辟或释放存储单元 malloc函数 calloc函数 free函数 应包含malloc.h或stdlib.h C语言程序设计 - 第8章 结构体
malloc函数 函数原型 参数 返回值 typedef unsigned size_t; void *malloc(size_t size); 参数 size:分配存储空间的字节数 返回值 若成功,返回指向分配区域起始地址的指针 若失败,返回NULL C语言程序设计 - 第8章 结构体
calloc函数 函数原型 参数 返回值 void *calloc(size_t n, size_t size); n :分配内存的项目数 若成功,返回指向分配区域起始地址的指针 若失败,返回NULL C语言程序设计 - 第8章 结构体
free函数 函数原型 参数 说明 void free(void *ptr); ptr:要释放的内存区地址 释放prt指向的内存区 释放后的内存区能够分配给其他变量使用 C语言程序设计 - 第8章 结构体
ai 结构体的应用—链表 (Link List) ... a1 a2 a3 an ^ head struct node { int data; struct node *next; }; struct node *head; struct node data next ai C语言程序设计 - 第8章 结构体
链表的操作 链表的建立 链表的遍历 删除结点 插入结点 从链尾到链头:新结点插入到链头 从链头到链尾:新结点插入到链尾 根据一定的条件,删除一个或多个结点 插入结点 根据一定的条件,把新结点插入到指定位置 C语言程序设计 - 第8章 结构体
建立链表 (从链尾到链头) ... ① for(i=0; i<n; i++) head ai-1 ai ④ head = p; ③ p->next = head; ② p = malloc(sizeof (struct node)); p->data = a[i]; C语言程序设计 - 第8章 结构体
建立链表 (从链头到链尾) ... ① for(i=0; i<n; i++) p ⑤ p = q; ai-1 ^ ai ^ q ③ q->next = NULL; ④ p->next = q; ② q = malloc(sizeof (struct node)); q->data = a[i]; C语言程序设计 - 第8章 结构体
遍历链表 ... ... ① while(p) ③ p = p->next; p ai-1 ai ai+1 ② printf("%d", p->data); C语言程序设计 - 第8章 结构体
删除结点 ... ... ① if(p->next满足删除条件) ② q = p->next; p q ④ free(q); ai-1 ai ai+1 ③ p->next = q->next; C语言程序设计 - 第8章 结构体
插入结点 ... ... p ① if(p满足插入条件) ai ai+1 q x ④ p->next = q; ③ q->next = p->next; ② q = malloc(sizeof (struct node)); q->data = x; C语言程序设计 - 第8章 结构体
链表操作中需要注意的几个问题 注意考虑几个特殊情况下的操作 最后一个结点的next指针应为NULL 链表为空表 (head==NULL) 链表只有一个结点 对链表的第一个结点进行操作 对链表的最后一个结点进行操作 最后一个结点的next指针应为NULL 可以定义一个结构体类型用于表示结点的数据部分,以便于对数据的操作 C语言程序设计 - 第8章 结构体
结束 The End 改编自白雪飞老师的 presentations, 感谢白老师! C语言程序设计 - 第8章 结构体