第十一章 结构体
11.1 概述
11.2 定义结构体变量的方法
11.3 结构体变量的引用
11.4 结构体变量的初始化
11.5 结构体数组
11.6 指向结构体数据的指针
11.7 用指针处理链表
11.1 概述在实际问题中我们常需要 把不同类型的几个数据组合起来,构成一个整体 。如一个公司职员的个人信息,或学校中教师和学生的信息。
以学生信息为例,它可能包括学生的学号、班级、姓名、性别、年龄、
成绩等。这时原有的那些数据类型就显的有点无能为力了,所以引入一种新的数据类型 ----结构体 。
结构体是由一些逻辑相关,但数据类型不同的分量组成的一组数据。
注意,用户需要先定义结构体类型,之后才能定义结构体变量 注意不要忘了分号称成员表列结构体类型定义形式,struct 结构体类型名
{ 数据类型 成员名 1;
数据类型 成员名 2;
:,
数据类型 成员名 n;
} ;
关键字用户定义的标识符
11.2 定义结构体变量的方法一,定义结构体变量
1,先定义结构体类型,
再定义变量
struct student
{ char name[10] ;
int age ;
float s1,s2 ;
} ;
struct student st1,st2 ;
st1
st2
name
age
s1
s2
name
age
s1
s2结构体变量 st1和 st2各自都需要 20个字节的存储空间
2,定义结构体类型同时定义变量
struct student
{ char name[10] ;
int age ;
float s1,s2 ;
} st1,st2 ;
3,直接定义结构体变量
struct
{ char name[10] ;
int age ;
float s1,s2 ;
} st1,st2 ;
4,说明,
(1) 结构体变量具有结构体类型的一切特征在内存中结构体变量占有一片连续的存储单元存储单元的字节数可用 sizeof 运算符 算出
printf(“%d\n”,sizeof(struct student) ) ;
printf(“%d\n”,sizeof(st1) ) ;
(2) 结构体类型可以嵌套定义例,struct date
{ int year ;
int month ;
int day ;
} ;
struct stud
{ char name[10] ;
struct date birthday ;
float s1,s2 ;
} ;
或,struct stud
{ char name[10] ;
struct date
{ int year ;
int month ;
int day ;
} birthday ;
float s1,s2 ;
} ;
11.3 结构体变量的引用
1,引用结构体变量中的成员格式,结构体变量名,成员名
struct student
{ char name[10] ;
int age ;
float s1,s2 ;
} ;
注意,一般是对结构体变量的各个成员分别进行赋值
st1 = {,Mary”,21,78,86 } ; 这样的赋值是不允许的
struct student st1 ;
st1,name =,Mary” ;
st1,age = 21 ;
st1,s1 = 78 ;
st1,s2 = 86 ;
struct date
{ int year ;
int month ;
int day ;
} ;
struct stud
{ char name[10] ;
int age ;
struct date birthday;
float s1,s2 ;
} ;
struct stud st2 ;
int age,year ;
st2,name =,John” ;
st2,age = 20 ;
st2,birthday,year = 1980 ;
st2,birthday,month = 11 ;
st2,birthday,day = 23 ;
st2,s1 = 89 ;
st2,s2 = 95 ;
age = 24 ;
year = 2000 ;可以定义与结构体变量成员名相同名字的变量它们之间不会发生混乱
2,相同类型的结构体 变量 可以进行 整体赋值
struct date
{ int year ;
int month ;
int day ;
} ;
struct stud
{ char name[10] ;
int age ;
struct date birthday;
float s1,s2 ;
} ;
struct stud st1,st2,st3;
st1,name =,John” ;
st1,age = 20 ;
st1,birthday.year = 1980 ;
st1,birthday.month = 11 ;
st1,birthday.day = 23 ;
st1,s1 = 89 ;
st1,s2 = 95 ;
st2=st1;
st3,name=“Mary”;
st3,age=20;
st3,birthday=st1,birthday;
st3,s1 = 76;
st3,s2 = 85;
注意要正确赋值的条件是变量 st1已经有了数据
3,结构体变量的输入 输出
C语言不允许结构体变量整体进行输入和输出,
只能对结构体变量的 成员 进行输入和输出
gets( st1,name ) ;
scanf(,%d%d%d”,&st1,birthday,year,
&st1,birthday,month,&st1,birthday,day ) ;
scanf (,%d%f%f”,&st1,age,&st1,s1,&st1,s2 ) ;
puts( s1,name ) ;
printf(,%4d”,st1,age );
printf(,%d,%d,%d”,st1,birthday,year,
st1,birthday,month,st1,birthday,day ) ;
printf(“%5.2f %5.2f\n”,st1,s1,s1t,s2 ) ;
11.4 结构体变量的初始化
struct student
{ char name[10] ;
int age ;
float score1,score2 ;
} st1={,Mary”,21,78,86} ;
struct stud
{ char name[10] ;
struct date birthday ;
float score1,score2 ;
} ;
struct stud st2={,John”,1980,11,23,89,95 } ;
struct student
{ char name[10] ;
int age ;
float score1,score2 ;
} ;
struct student st1;
st1={,Mary”,21,78,86} ;这是初始化,正确这是赋值,错误
C不允许这么做
11.5 结构体数组一,结构体数组的定义
1,先定义结构体类型再定义结构体数组
struct student
{ char name[10] ;
int age ;
float s1,s2 ;
} ;
struct student st[6] ;
2,定义结构体类型的同时定义数组
struct student
{ char name[10] ;
int age ;
float s1,s2 ;
} st[6] ;
3,直接定义结构体数组
struct
{ char name[10] ;
int age ;
float s1,s2 ;
} st[6] ;
二、结构体数组的初始化将每个数组元素的数据用花括号 { } 括起来
struct student
{ char name[10] ;
int age ;
float s1,s2 ;
} ;
struct student st[3]={ {“Mary”,21,78,86},
{“Alex”,20,90,80},{“Mike”,19,75,68} };
Mary
21
78
86
Alex
20
90
80
Mike
19
75
68
st[0]
st[1]
st[2]
2,数组元素之间可以整体赋值也可以将一个元素赋给一个相同类型的结构体变量
struct student x,st[3]={ {“Mary”,21,78,86},{“Alex”,…} } ;
st[2] = st[0] ;
x = st[1] ;
3,只能对数组元素的 成员 进行输入和输出
gets( st[2],name ) ;
scanf(“%d”,&st[2],age ) ;
scanf(“%f%f,,&st[2],s1,&st[2],s2 );
puts( st[0],name );
printf(“%4d%5.2f %5.2f\n”,st[0],age,st[0],s1,st[0],s2) ;
都是结构体变量的整体赋值三,结构体数组的引用
1,引用某个数组元素的成员 例,puts( st[0],name ) ;
printf(“%d,%d”,st[1],age,st[1],s1 ) ;
例,有 30名学生,每个学生包括学号、姓名、成绩,
要求找出成绩最高者,并输出他的信息
#include <stdio.h>
#define N 30
void main( )
{ struct student
{ int n ;
char name[10] ;
int score ;
};
struct student st[N];
int i,m ;
int max ;
for ( i=0 ; i<N ; i++)
scanf(“%d%s%d”,&st[i].n,
st[i].name,&st[i].score) ;
max=st[0].score;
for( i=1 ; i<N ; i++ )
if ( st[i].score > max )
{ max=st[i].score;
m=i; }
printf(“%4d”,st[m].n ) ;
printf(“%10s,,st[m].name );
printf(“%5d,,st[m].score);
}
例,按成绩对学生信息进行从高到底的排序
#include <stdio.h>
#define N 30
struct stud
{ int n ;
char name[10] ;
int s ;
};
void input(struct stud a[ ])
{ int i ;
for ( i=0 ; i<N ; i++)
scanf(“%d%s%d”,&a[i].n,
a[i].name,&a[i].s) ;
}
void output(struct stud a[ ])
{ int i ;
for ( i=0 ; i<N ; i++)
printf(“%4d%10s%4d”,
a[i].n,a[i].name,a[i].s) ;
}
void sort(struct stud a[ ] )
{ int i,j ;
struct stud temp;
for ( i=0 ; i<N-1 ; i++)
for ( j=i+1 ; j<N ; j++)
if ( a[i].s<a[j].s )
{ temp=a[i] ;
a[i]=a[j] ;
a[j]=temp ;
}
}
void main( )
{ struct stud st[N];
input(st);
sort(st);
output(st);
}
11.6 指向结构体数据的指针一,指向结构体变量的指针
1,定义
struct student
{ char name[20] ;
int age ;
float s1,s2 ;
} ;
struct student stu,*p ;
p = &stu ;
2,成员的引用格式
(1) 结构体变量名,成员名
stu,name
gets( stu,name );
(*p),age = 21 ;
p -> s1 = 87 ;
p -> s2 = 90 ;
(2) (*指针变量名 ),成员名
(*p),age
(3) 指针变量名 -> 成员名
p -> s1
二,指向结构体数组的指针
1,定义 struct student a[3],*p ;
2,使用 for ( p=a ; p<a+3 ; p++ )
{ gets( p->name ) ;
scanf(,%d%d %d”,&p->age,&p->s1,&p->s2) ;
}
三,结构体变量作为函数参数
1,函数的实参和形参都用结构体变量,参数之间为 值传递即,实参结构体变量 各成员的值依次传给 形参结构体变量
2,返回结构体类型值的函数函数定义格式,结构体类型名 函数名 ( 形参表列 )
{ 函数体 ; }
例,struct student funct ( int x,float y )
{ 函数体 ; }
注意 结构体类型是已经定义好的例,求学生成绩的总分和平均分
#include <stdio.h>
#define N 5
struct stud
{ char name[10] ;
int s[3] ;
float sum,ave ;
} ;
struct stud count (struct stud x)
// 结构体变量做形参,函数返回值为结构体类型
{ int j ;
x.sum = 0 ;
for ( j = 0; j<3 ; j++ )
x.sum = x.sum + x.s[j] ;
x.ave = x.sum / 3 ;
return(x);
}
void main ( )
{ struct stud a[N] ;
int j ;
for ( j=0; j<N; j++ )
{ scanf(“%s%d,,a[j].name,&a[j].s[0] );
scanf(“%d %d,,&a[j].s[1],&a[j].s[2] );
}
for ( j=0; j<N; j++) a[j]=count ( a[j] ) ;/*结构体数组元素名作实参 */
for ( j=0; j<N; j++)
{ printf(“%10s%4d”,a[j].name,a[j].s[0] );
printf(“%4d%4d”,a[j].s[1],a[j].s[2] );
printf(“%8.2f %6.2f\n”,a[j].sum,a[j].ave );
}
}
11.7 用指针处理链表一,、基本概念
1,动态存储分配,根据需要临时分配内存单元用以存放数据,
当数据不用时可以随时释放内存单元
2,链表,是可以动态地进行存储分配的一种数据结构它是 由一组动态数据链接而成的序列
3,结点,链表中的每一个动态数据称为一个结点
4,结点类型,是一个包含指针项的结构体类型一般由两部分组成,
(1) 数据成员,存放数据
(2) 指针成员,存放下一个结点的地址
struct sd
{ int num;
int score;
struct sd *next ;
} ;
数据成员指针成员头指针 表头结点 表尾结点
2010
head 2010 1428 1570
95
1428
1
86
1570
2
82
NULL
3
NULL为空地址,
表示链表到此结束二、简单链表
#include <stdio.h>
struct sd
{ int num;
int score;
struct sd *next ;
} ;
void main( )
{ struct sd a,b,c,*head,*p;
head=&a;
a.num=1; a.score=95; a.next=&b;
b.num=2; b.score=86; b.next=&c;
c.num=3; c.score=82; c.next=NULL;
p=head;
while(p!=NULL)
{ printf(“%3d%4d\n”,p->num,p->score);
p=p->next;
}
}
head 2010 1428 1570
2010
a b c
1
95
1428
2
86
1570
3
82
NULL
p 201014281570NULL
说明,该程序虽然建立了一个链表,但这个链表是静态的,因为它的结点个数是固定的,不能按需要增加新的结点,也不能按需要删除结点,这样的链表并不实用,我们需要的是“动态链表”
三,处理动态链表所需的函数 ( 需用头文件 <stdlib.h> )
1,malloc 函数原型,void *malloc( unsigned int size )
作用,在内存中开辟一个长度为 size 的连续存储空间,
并将此存储 空间的起始地址带回注意,
(1) 函数返回值是指针,但该指针是指向 void类型的,因此在使用时希望这个指针指向其他类型需要用强制类型转换
(2) 如果内存缺少足够大的空间进行分配,则 malloc 函数返回值为“空指针” (即 NULL)
例,struct sd *p;
p = (struct sd * ) malloc ( sizeof(struct sd) ) ;
强制类型转换结构体类型占用的字节长度
2,free函数原型,void free( void *ptr )
作用,将指针变量 ptr指向的存储空间释放注意,
(1) ptr的值不是任意的地址,必须是程序中执行 malloc或
calloc函数所返回的地址
(2) 模型中 ptr是 void型,但调用 free函数时,参数可能是其他类型,计算机系统会自动进行转换例,struct sd *p;
p=(struct sd *) malloc (sizeof(struct sd)) ;
free(p); p 42004200
四,链表应用举例
1,建立链表 (表尾添加法 )
#include <stdio.h>
#include <stdlib.h>
#define ST struct student
ST
{ int num ;
int score ;
ST *next ;
};
#define LEN sizeof(ST)
int n ;
宏定义 ST,以后书写简单求出结构体类型占用的字节数全局量,n 表示结点的个数创建一个由 head指向的链表
ST * creat(void)
{ ST *head,*p1,*p2 ;
n=0 ; head=NULL ; p1=(ST *) malloc ( LEN ) ;
scanf(,%d %d”,&p1->num,&p1->score ) ;
while ( p1->num!=0 )
{ n=n+1;
if (n= =1) head=p1 ;
else p2->next=p1 ;
p2 = p1 ;
p1 = (ST *) malloc (LEN) ;
scanf(,%d %d”,&p1->num,&p1->score ) ;
}
p2->next = NULL ; return( head ) ;
}
head 2010 1428 1570
NULL
p1 p2
3264
2010
2010
1
95
1428
n 0123
20101428
2
86
3
82
0
0
1428 1570 NULL
1570 15703264
2,输出链表
(将 head指向的链表从头到尾输出一遍)
void list ( ST *head )
{ ST *p ;
p = head ;
while ( p!=NULL )
{ printf(“%3d %4d\n”,p->num,p->score ) ;
p = p->next ;
}
}
head 2010 1428 1570
2010 1
95
1428
2
86
1570
3
82
NULL
p 201014281570NULL
p1 20101428
num 2
p2 2010
head 2010 1428 1570
2010 1
95
1428
2
86
1570
3
82
NULL1570
n 32
3,链表的删除
(在 head指向的链表中,删除数据域为 num的结点)
ST *del ( ST *head,int num )
{ ST *p1,*p2 ;
p1 = head ;
while ( ( num!=p1->num) && (p1->next!=NULL) )
{ p2 = p1 ; p1 = p1->next ; }
if ( num = = p1->num )
{ if ( p1= =head ) head=p1->next ;
else p2->next=p1->next ;
free( p1 ) ; n=n-1 ;
printf(,deleted ! \n”) ;
}
else printf(,can not delete!\n”) ;
return( head ) ;
}
4,链表的插入
ST *insert ( ST *head )
{ ST *p0,*p1,*p2 ;
p1=head ;
p0=( ST *) malloc ( LEN ) ;
scanf (“%d%d”,&p0->num,&p0->score) ;
if ( head= =NULL)
{ head = p0 ; p0->next = NULL ; }
else { while ( ( p0->num > p1->num ) && ( p1->next!=NULL) )
{ p2 = p1 ; p1 = p1->next ; }
if ( p0->num<p1->num )
{ if ( head= =p1) head = p0 ; else p2->next =p0 ;
p0->next = p1 ;
}
else { p1->next = p0 ; p0->next = NULL; }
}
n++ ; return( head ) ;
}
2010 1428 1570head
2010 1
95
1428
3
86
1570
5
82
NULL
n 34
p1 p2
p0
2680
2680
2010 2010 4
75
14281570 1428
2680
1570
2010 1428 1570head
2010 2
95
1428
3
86
1570
5
82
NULL
n 34
p1 p2p0
2680
2680 2010
1
75
插入的结点作为表头
2010
2680
2010 1428 1570head
2010 2
95
1428
3
86
1570
5
82
NULL
n 34
p1 p2 p0
2680
26802010
8
75
插入的结点作为表尾
NULL
20101428 14281570
2680
5,main 函数
void main ( )
{ ST *h ;
int delnum ;
h = creat( ) ;
list ( h ) ;
scanf(“%d”,&delnum ) ;
h = del ( h,delnum ) ;
list ( h ) ;
h = insert ( h ) ;
list ( h ) ;
}
/* 建立一个链表 */
/* 输出链表 */
/* 输入要删除的学号 */
/* 删除学号为 delnum的结点 */
/* 在链表中插入一个结点 */
课后作业,P318
11.3 11.5