第八章 结构体 (与共用体 )
★ 内容提要:
?结构体类型定义
?结构体变量定义引用
?结构体数组与指针
?结构体与函数
?结构体数组应用
?结构体与链表应用
结构体类型的定义
结构体变量的定义与引用
结构体数组与指针
结构体与函数 (数据传递 )
结构体数组应用
结构体与链表应用
结构体(结构变量)是不同类型的数
据元素的有序集合。相当于其它高级语
言的记录型,用以组织和处理复杂的数
据 ( 客观世界的实体 ) 。
结构体(结构变量)是不同类型的数
据元素的有序集合。相当于其它高级语
言的记录型,用以组织和处理复杂的数
据 ( 客观世界的实体 ) 。
实体,指客观世界的人、事、物、概念等。
属性,实体的特征,用以描述实体。
8.1 实体与属性
学生是个实体,可以通过以下属性给以描述:
实体,
属性 组:
学生
nu m add r score sex age nam e
学生是个实体,可以通过以下属性给以描述:
实体
属性 组:
学生实体
属性 组:
学生
struct student {
int num;
char name[20];
char sex;
int age;
float score;
char addr[32];
};
11.2 定义结构体类型
str uct stu den t 两者
构成结构体特定类型构成结构体特定类型
属性描述,包括属性
个数、所属类型、存
储顺序、所占存储空
间的大小等,称为成
员(属性)说明表。
属性描述,包括属性
个数、所属类型、存
储顺序、所占存储空
间的大小等,称为成
员(属性)说明表。
struct 结构体名 {
类型名 1 成员名 1;
类型名 2 成员名 2;
?? ;
类型名 i 成员名 i;
?? ;
类型名 n 成员名 n;
};
结构体类型定义形式,
struct 结构体名 两者
构成结构体特定类型构成结构体特定类型
属性描述,包括属性
个数、所属类型、存
储顺序、所占存储空
间的大小等,称为成
员(属性)说明表。
属性描述,包括属性
个数、所属类型、存
储顺序、所占存储空
间的大小等,称为成
员(属性)说明表。
类型定义只是说明了一个 实体相应的属性描
述,只有通过定义相应的变量,并赋以一定的
值才能构成一个实体的元素(记录)。
结构变量的存储单元的大小为各成员
所需容量的总和。
以本例说明:
结构变量的存储单元的大小 = 4+20+1+4+4+32=65
struct student student1;
8.3 定义结构体变量及常用方式
1) 先定义结构体类型再定义相应变量:
编译时系统为其分配存储空间:
stu dent 1
num nam e[ ] add r[ ]scoreagesex
struct student {
int num;
char name[20], s e x;
int age;
float score;
char addr[32]
} student1,student2;
2) 在定义结构体类型的同时定义变量:
3) 结构体类型可以嵌套定义:图书是个实体,
可以通过以下属性给以描述:
实体,
属性 组:
图书
mont h
价格
出版日期
出版社书名作者
year day
实体
属性 组:
struct date{
int month,day,year;
}
struct book{
c h a r a u t h o r [ 1 6 ],b o o k n a m e [ 3 2 ],p u b l i s h [ 6 0 ] ;
struct date publishdate;
float price;
} book1;
8.3 定义结构体变量及常用方式
注意:
? 正确区分类型与变量的概念,只能对变 量赋
值、存取或运算,不能对一个类型赋值、存 取
或运算;
? 为书写方便,可利用宏定义:
# define STUDENT struct student
类似于对数组的初始化。传统的 C 规定,只
有全局的或局部静态 的结构变量才能初始化,
不能对动态局部结构变量进行初始化;新的
A N S I C 和标准 C 的版本中取消了上述 限制,允
许对自动结构变量进 行初始化,但初始化的时
间是在相应函数执行时进行。
8.4 结构变量的初始化
8.4 结构变量的初始化
对于需要初始化的结构变量最好是把
它定义为静态变量。其它的在函数执行
时用赋值语句或通过 scanf 函数对各成员
分别赋值。
1,外部的结构变量初始化(全局变量) 。
struct student{
int num;
char name[20],sex,addr[20];
}a={0307001,"zhang san",'m',"123ShangHaiRood"};
或先有类型,再定义说明结构变量 。
struct student a = {
0307001,"zhang san",'m',"123ShangHaiRood"};
2,静态的局部结构变量初始化 。
main(){
static struct student{
int num;
char name[20],sex,addr[20];
}a={0307001,"zhang san",'m',"123BeijinRood"};
printf("N0.:%d \ nname:%s \ nsex:%c \ naddr:%s \ n",
a.num,a.name,a.sex,a.addr);
}
1, 表达式中只能以结构变量成员的方式引用,即不
能将一个结构变量作为整体引用,只能对结构体变量
总(最低层)的各个成员分别引用。
引用方式,结构变量名, 成员名
8.5 结构变量的引用规则
‘, ’ 为成员运算符,属于优先级最高的运算符。
,结构变量名, 成员名, 相当于一个变量。
2,如果结构变量为 嵌套结构,就应使用成
员运算符层层结合,一直找到最低一级的成员,
即只能对最低一级的成员进行赋值或存取运算。
book1.publishdate.month
book1.publishdate.day
book1.publishdate.year
3,成员属变量,故可项变量一样进行各种
操作。
sum=student1.score+student2.score;
student1.age++;
4,可以引用成员的地址,也可以引用结构
变量的地址。
scanf(, %d,,&student1.num);
printf(, %u %u \ n,,
&student1,&student1.num);
结构数组是以同类型结构变量为元素的有序
集合,每一变量(元素)都分别包括各自 的成
员数据项。
struct student stud1[40];
8.6 结构数组
结构数组初始化基本规则同其它类型的数组,
struct student stud1[40]={{ ? },
{ ? }, ?, { ? }} ;
结构变量被定义之后,编译时就为其在内存
中分配一片连续的存储单元。该片内存单元 的
起始地址称为该结构变量的指针。可以设立 一
个指针变量,用来存放这个地址。当把 一个结
构变量的起始地址赋予一个指针变量时,就称
该指针指向这个结构变量。
8.7 指向结构体类型数据的指针
1,指向某一结构的指针的说明方式:
struct 结构名 * 变量名;
struct { 成员说明表列 } * 变量名;
2,将一个函数的返回 值说明为指向某结构的
指针的说明方法:
struct 结构名 * 函数名 ( 形参说明表列 ){
?? // 函数体
}
指针的说明方法:
3,关于指向成员运算符 ‘ - > ’,若有说明:
struct student *p,a; p=&a; ??
则根据指针的运算规则,对 p 所指向的结构变
量 a 的分量 num 的引用方式可以如下:
(* p).nu m
p - >num
a.num
等价
只是存取方式不同
等价
4,结构与函数
早期的 C 不允许用结构变量作为 函数的参数,
解决结构变量的传递的办法:
? 用结构变量的成员作参数,属, 传值, 方式;
? 用取地址运算符 ‘ & ’ 取结构变量的地址,
将指向结构变量 ( 或数组 ) 的指针作实参,属
,传地, 方式;
? 函数的返回值也可以是一个指向结构的指针。
根据 A N S I C 的新标准规定,不仅允许 使用指
向结构体变量的指针作为函数 的参数,还允许
直接使用结构体变量作为函数 的参数,但此时
为传, 传值, 方式,将结构变量所占的内存单
元全部顺序传递(复制)给形 参。形参也必须
是同类型的结构变量。
struct student{ // 指向结构体变量的指针示例
int num; char name[20]; float score[3]; };
void print(struct student *p){
printf("%d \ n%s \ n%f \ n%f \ n%f \ n",
p - >num,p - >name,p - >score[0],
p - >score[1],p - >score[2] );
}
void main(){
struct student stu={
9507001,"xiao li",67.5,89.0,95.0 };
print( &stu );
}
p
xiao li
67.5
89.0
95.0
0507001
& stu
struct score_list{int num; float score; };
float average(struct score_list *p){
float x; int i;
for(i=0,x=0.0;p - >num!=0;p++,i++)
x=x+p - >score;
x=x/i; return(x);
}
void main(){
static struct score_list a[4]={
0507001,98.5,0507002,90.0,
0507003,95.0,0,0 };
z=average( a );
printf("average is %f \ n",z );
}
p
a
98.5
0507002
90.0
0507003
0507001
95.0
0
指向结构体
数组的指针
0
指针 p 是指向结构的指针,
对 p 所施加的运算均是按结构
体的实际长度计算的,因此
p++ 总是指向结构数组的下一
个元素,而不是指向某一元素
中的某一成员。若地址类型不
同,可进行强制类型转换:
p=(struct score_list *)&a.num
p
a
98.5
0507002
90.0
0507003
0507001
95.0
0
指向结构体
数组的指针
0
已学类型与数据的组织形式,
1,基本类型,
int,char, float, double 用于定义相应的单值变量。
2,构造类型:
数 组 -- 同类型数据元素的有序集合,适合存储与
处理相关的同类型的数据。
结构体 -- 不同类型的数据元素的有序集合,适合存
储处理相关的不同数据类型构成的实体。
8.8 结构体数组应用
学习各种类型的目的是:在编程
时能根据数据组织形式,选择 最佳
的存储结构 ( 类型 ) 和算法求解问题。
3,指针类型 -- 提供了通过地址间接访问存储单元的方
式。
结构体应用之一(编程要点、步骤与算法示例)
题目,定义一个结构数组,描述 40 个学生的学号、姓
名、三门课程的成绩及平均成 绩,并以函数形式实现
以下功能:
? 读入学生的前五项数据;
? 计算平均成绩;
? 按平均成绩以递减顺序排序;
? 打印输出排序后的成绩表。
程序设计:
1 )分析题意明确要求
? 数据描述,定义一个结构数组,其元素可 有六个
成员组成,
学生 ( num,n ame[16],score 1,score2,scor e3,aver age )
int char 类型相同可采用 int score[4] 表示
? 定义四个功能函数,考虑确 定相关的功能和数据
传递方式, 本例采用形实参数结合的形式。
? 结构类型应在外部定义,以便各函数应用定义相
应的变量;
? 可考虑适当的采用符号常数形式,方便程序的书
写、阅读和调试。
input:
050101 zhang 90 90 90 ↙
050102wang 92 92 92 ↙
050103li 98 98 98 ↙
050104zhao 95 95 95 ↙
output:
050103 zhang 98 98 98 98
050104 wang 95 95 95 95
050102 li 92 92 92 92
050101 zhao 90 90 90 90
静态数据结构, 前面讨论的各种基本类型和 组
合类型的数据都属静态数据结构,它们所占存
储空间的大小在程序的说明部分 就 已经确定,
如变量、数组、结构等,它们不能在程序的执
行过程中加以改变。
动态数据结构, 它是在程序的执行过程中动 态
的 建立起来的,故这种数据结构的规模大小在
程序执行期间可动态地变化。
8.9 用指针处理链表(结构应用之二)
利用动态数据结构可以解决一些静态数据结构
难以解决的问题。如想在数组中插入或删除一
个元素就比较困难,而动态数据结构就能方便
地解决这类问题。
动态数据结构中最基本的形式是链表和二叉树
,它们在应用软件和系统软件的设计中非常有
用。
一、动态存储分配函数
1, malloc() 函数,(函数原型如下)
void *malloc( unsigned size );
功能, 分配 size 字节的存储区,返回值为所分
配内存区的起始地址,如不成功,则返回 0 。
一、动态存储分配函数
2, c a l l o c( ) 函数,(函数原型如下)
v o i d * c a l l o c ( u n s i g n e d n,u n s i g n e d s i z e )
功能, 分配 n 个数据项的内存连续空间,每个
数据项的大小为 s iz e 。 返回值为所分配内存单
元的起始地址,如不成功,则返回 0 。
3, free() 函数,(函数原型如下)
void free( void *p );
功能,释放 p 所指的内存区,返回值无。
说明,ANSI 标准要求动态分配系统返回 void 指
针。但目前大多数 C 编译所提供的这类函数都
返回 char 指针。 无论以上情况的哪一种,都需
要用强制类型转换的方法把它们转换为所需要
的类型。
二,建立链表示例
链表是一种最简单的 动态数据结构,利用它可以在
一张表中随机地插入和删除元素,可形象地表示为:
学生信息表( d a t a 1,d a t a 2,d a t a 3,……, dataN )
头指针
head
D ata1 D ata2 D ata3 D ata
N
NULL
从无到有在程序运行过程 中根据需要申请一个结点
空间,输入数据就建立一个。它使用递归结构的方法
来建立动态数据结构,利用结构类型可用来描述链表
的元素(结点)。
例:以链表形式处理学生信息(建立链表、遍历打印、
删除、插入和 main() 主函数)。
头指针
head
D ata1 D ata2 D ata3 D ata
N
NULL
# include <stdio.h>
#include <malloc.h>
#define STUDENT struct student
#define LEN sizeof(STUDENT)
STUDENT{ // 定义结构体(结点)的类型
long num;
float score;
STUDENT *next;
};
int n; // 全局变量 n 用来统计链表结点的个数
// 遍历打印
98101 98.000000
98103 87.000000
98105 77.000000
# include <stdio.h>
#include <math.h>
#define EPS 1e - 7
void main(){
double udf_sin(double x); // 用户自定义函数原型说明
double a; scanf("%lf",&a);
printf("%f %f \ n",udf_sin(a),sin(a));
}
double udf_sin( double x ){ // 用户自定义函数
double sum,term,n=1; sum=term=x;
while( fabs(term) > EPS ){
n=n+1;
term=term*( - x*x)/((2*n - 2)*(2*n - 1));Th e end
培 育 英 才 钻 研 科 学
书山有径勤为路
学海无边苦作舟
书山有径勤为路书山有径勤为路
学海无边苦作舟学海无边苦作舟