文章目录
9.1定义和使用结构体变量
9.1.1用户建立结构体类型
首先说一下什么是结构体类型,就是把咱们之前学过的,系统提供的变量,来一个复合打包。
比如
int a;
char b;
float c;
double d;
long long e;
现在我打包起来
struct student
{
int a;
char b;
float c;
double d;
long long e;
}
为什么这样做呢,我们一般解决实际问题是,很多不同类型数据都存在相关练习。比如我描述一个人,可以从年龄,性别,学历,工作,四个角度去描述一个人,这四个不同变量类型。往往存在一定的联系。那我们把这些数据打包,组成一个组合数据,这样我们就能够用一个数据表示所有的情况,是不是很方便,精炼。
用户自己建立有不同类型数据组成的组合型数据结构,称之为结构体
一般形式:
struct 结构体名
{成员表列};
结构体名也叫“结构体标记”。成员列表也成为“域表“;
域表元素的命名规则: 类型名 成员名;
可以同时建立多个结构体,并且一个结构体可以是另外一个结构体的成员。
struct Date
{
int month;
int day;
int year;
};
struct student
{
int num;
char name[20];
char sex;
int age;
struct Date birthday;
char addr[30];
}
9.1.2 定义结构体类型变量
这里我们学习如何在程序中使用结构体变量;总的包括,定义结构体变量,然后初始化,最后能存取数值。这里我先讲解如何定义;
1.先声明结构体类型,再定义该类型的变量
struct Date
{
int month;
int day;
int year;
};
struct student
{
int num;
char name[20];
char sex;
int age;
struct Date birthday;
char addr[30];
}//先声明结构体类型
struct student student1,student 2;//然后定义该类型的变量。
定义了两个变量,定义的类型式没有储存空间的,只有定义变量才有储存空间。
现在变量student 1,student 2;的结构如下图。
2.在声明类型的同时定义变量
struct Date
{
int month;
int day;
int year;
};
struct student
{
int num;
char name[20];
char sex;
int age;
struct Date birthday;
char addr[30];
} student 1,student 2;
一般形式:
struct 结构体名
{
成员列表
}变量名列表;
3.不指定类型名而直接定义结构体类型变量
一般形式:
struct
{
成员列表
}变量名列表;
这种方法的局限性在于,他只能定义变量名列表的变量,由于没有变量名,所以不能定义其他变量为此结构体了。
注意:
a.结构体类型和结构体变量完全是两个不同的概念;
b.结构体中的成员名比如num,和程序中的num完全是不同的概念。名字相同,但是表达的意义是不相同的。
c.结构体中的成是可以当作普通变量去使用的,可以运算,赋值等等,怎么引用,一般是结构变量名.成员名
9.1.3结构体变量的初始化和引用
看个例子;
/*把一个学生的信息(学号,姓名,性别,住址)放在一个结构体中,然后输出这个学生的信息*/
#include <stdio.h>
int main()
{
struct student
{
long int num;
char name [20];
char sex;
char addr[20];
}a={10100,"wu mingda",'M',"XIN DE BAN DAO"};
printf("NO.:%1d\nname:%s\nsex:%c\naddr:%s\n",a.num,a.name,a.sex,a.addr);
}
从上面的例子咱们可以看出,如何初始化一个结构体变量。
a={10100,"wu mingda",'M',"XIN DE BAN DAO"};
如果想对某一起其中的元素单独初始化也可以(C99以上),如下:
={.num=1};//{a.name=1};也行
/*把一个学生的信息(学号,姓名,性别,住址)放在一个结构体中,然后输出这个学生的信息*/
#include <stdio.h>
int main()
{
struct student
{
long int num;
char name [20];
char sex;
char addr[20];
}a={.num=1};//{a.name=1};也行
printf("NO.:%1d\nname:%s\nsex:%c\naddr:%s\n",a.num,a.name,a.sex,a.addr);
}
其他元素没有被初始化的元素首先就是,数值型=0;字符型=\0;指针型:NULL;
如何引用结构体变量名字
结构体变量名.成员名
. 是成员运算符,优先级最高。
赋值:a.num=10010;同类型结构体变量的可以相互赋值;
可以进行各种运算;
可以引用结构体变量的地址:&student1;
也可以引用结构体变量成员名的地址:&student1.num;
如果成员本身就是一个结构体,如何引用呢;
student1.num;
student1.birthday.month;
注意结构体变量只能一个一个输出:
printf("NO.:%1d\nname:%s\nsex:%c\naddr:%s\n",a.num,a.name,a.sex,a.addr);
不能一次性输出,一次性读入:
printf("%s\n",student1);
scanf("%d,%s,%c,%s",%student1);
在看一个例题
输入两个学生的相关信息,输出成绩较高的那个人相关信息
#include <stdio.h>
int main()
{
struct student
{
int num;
char name[20];
float score;
}student1,student2;
scanf("%d%s%f",&student1.num,student1.name,&student1.score);
scanf("%d%s%f",&student2.num,student2.name,&student2.score);
printf("the higher score is:\n");
if(student1.score>student2.score)
printf("%d %s %6.2f\n",student1.num,student1.name,student1.score);
else if(student1.score<student2.score)
printf("%d %s %6.2f\n",student2.num,student2.name,student2.score);
else
{
printf("%d %s %6.2f\n",student1.num,student1.name,student1.score);
printf("%d %s %6.2f\n",student2.num,student2.name,student2.score);
}
return 0;
}
9.2使用结构体数组
9.2.1定义结构体数组
/*有三个候选人,每个选民只能投票一人,要求编一个统计票数的程序,先后输入被选人的名字
,最后输出各人的的票结果*/
#include <stdio.h>
#include <string.h>
struct person
{
char name[20];
int count;
}leader[3]={"Li",0,"zhang",0,"sun",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++;
/*
strcmp(字符串1,字符串2);
意思就是让两个字符串进行比较 ;如果两个比较等价;那么输出得结果就是0;
小于;那么输出的结果就是一个负整数;
大于;;;;;输出的结果就是一个正整数;
那么你坑定会问 ;怎么进行比较的呢;
首先自左向右进行比较;通过ascall
1)完全相同; 说明字符串完全相等;
2)有不同 以第一对为准;
3)如果两个字符串都是英语都是英文;大写小于小写;在英文字典在后面得位置为大;*/
}
printf("\nResult:\n");
for(i=0;i<3;i++)
printf("%5s:%d\n",leader[i].name,leader[i].count);
return 0;
}
定义结构体数组的一般形式:
struct 结构体名
{
成员表列
} 数组名[数组长度];
或者
先声明定义一个结构体类型 ;
结构体类型 数组名[数组长度];
例如
struct person
{
char name[20];
int count;
}leader[3]={"Li",0,"zhang",0,"sun",0};初始化1
或者:
struct person
{
char name[20];
int count;
};
struct person leader[3]={"Li",0,"zhang",0,"sun",0};初始化2
初始化就是在定义的变量后面直接加上初始化表列;
9.2.2 结构体数组的应用举例
/*有n个学生的信息(包括学号,姓名,成绩),要求按照成绩的高低
顺序输出各个学生的信息*/
#include <stdio.h>
struct student
{
int num;
char name[20];
float score;
} ;//
int main()
{
struct student stu[5]={ {10101,"zhang",78 },{10103,"wang",98.5},{10106,"Li",86},{10108,"ling",73.5},{10110,"sun",100}};//
struct student temp;
const int n=5;
int i,j,k;
printf("the order is:\n");
for(i=0;i<n-1;i++)
{
k=i;
for(j=i+1;j<n;j++)
if(stu[j].score>stu[k].score) k=j;
temp=stu[k];stu[k]=stu[i];stu[i]=temp;
}
for(i=0;i<n;i++)
printf("%6d %8s %6.2f\n",stu[i].num,stu[i].name,stu[i].score);
printf("\n");
return 0;
}
9.3 结构体指针
每个结构体都有一个地址,定义一个指针变量,他只能储存结构体类型的地址的话,我称之为结构体指针。结构体指针可以指向结构体。
9.3.1指向结构体变量的指针
首先咱们看一个例子;
/*通过指向结构体变量的指针变量输出结构体变量中的相关信息*/
#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");//字符串赋值函数;把"li lin"赋值stu.1.name;
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);
printf("no.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n",(*p).num,(*p).name,(*p).sex,(*p).score);//
return 0;
}
(*p).num;p是变量名字,包含地址,*p意思是指向结构体()优先级大于.num,
所以说先指向某一个结构体,然后就是找到里面对应的num;
如果没有括号,就是.num优先于*运算符,所以显示p.num;p是变量名字;
所以p.num没有意义;所以说必须加括号;
以下三种情况等价:
**
stu.num stu;本身相当于一个抽象的变量,所以就不代表地址的意思,直接就是数值
(*p).num;
p->num;->指向运算符。前面是地址,后面是具体那个元素;
**
9.3.2指向结构体数组的指针
#include <stdio.h>
struct student
{
int num;
char name [20];
char sex;
int age;
};
struct student stu[3]={{10101,"lilin",'M',18},{10102,"ZHANG FANG",'M',19},{10104,"WANGMIN",'F',20}};
int main()
{
struct student*p;
printf("no.name sex age\n");
for(p=stu;p<stu+3;p++)
printf("%5d %-20s %2c %4d\n",p->num,p->name,p->sex,p->age);
return 0;
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191226215618270.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTg0MDA4Nw==,size_16,color_FFFFFF,t_70
9.4 用指针处理链表
9.4.1 什么是链表
在前面的学习中,当我们处理数据,比如ABCD苏格版每个班级的人数都不一样,我们处理数据的方法是建立一个数组,但是也存在一定的问题,我们建立一个数组的时候,只能建立最长的那个班级人数的数组,这样相对于其他班级人数少的时候,是不是对于数组来说就造成储存空间的一种浪费。那么我们能不能建立一种动态储存空间,能随班级的人数来动态分配内存,能够需要来动态建立储存单元的结构叫做链表。
如图所示,链表必须要有一个
头指针 :只用来存放地址;
表尾:存放一个数据,而且其中存放指针地址的区域存放空指针,不知想任何类型。
看这个链表,去掉一头一尾部,剩下的中间那些称之为一个又一个的节点。
看这些节点,它包括两个东西:
a.用户需要用的实际数据
b.下一个节点的地址;
所以我们看一看链表有什么特点:
1.看上图,链表各节点之间的地址是可以不连续的。
2.结构体变量,用它去建立链表是最合适的。
3.在结构体中定义的某一个指针,根据定义的在指针类型,可以指向自己所在的结构体数据,也可以指向其他类型的结构体数据。
我们看一下:
struct student
{
int num;
float score;
struct student *next;//定义的结构体上面两行就是所谓的实际数据,下面指针定义的就是只能只想自己的数据类型。
}
9.4.2建立简单的静态链表
我们看一个例子,建立一个静态链表:
/*建立一个简单链表,有三个同学的数据节点组成,要求输出节点的数据*/
#include <stdio.h>
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=10103;b.score=90;
c.num=10107;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->意思就是指向b这个结构体了,然后·取num的数值。
p=p->next;//p是地址,->只想运算符,p->意思就是指向b这个结构体了,然后取next值
} while(p!=NULL);
return 0;
}
9.4.3 建立动态链表
/*写一个函数建立一个有三个学生数据的单向动态链表*/
#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct student) //测量字节
struct student
{
long num;
float score;
struct student*next;
};
int n;
struct student *creat(void)
{
struct student*head;
struct student*p1,*p2;
n=0;
p1=p2=(struct student*)malloc(LEN);//建立一个动态储存区返回一个地址,这个动态储存区的长度为LEN;
scanf("%ld,%f",&p1->num,&p1->score);
head=NULL;
while(p1->num!=0)
{
n=n+1;
if(n==1)head=p1;
else p2->next=p1;
p2=p1;
p1=(struct student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
}
p2->next=NULL;
return(head);
}
int main()
{
struct student *pt;
pt=creat();
printf("\nnum:%ld\nscore:%5.1f\n",pt->num,pt->score);
return 0;
}
里面出现了n.一开始n=0,然后n=1;n=2,3,4;
n=0;还没有建立链表;
n=1;建立了一个链表节段;赋值时把p1给head,让p2等于p1;
n=2;建立了第二个链表节段;赋值时让p1等于上一个变量中的next,p2等于此时的p1;
所以n可以作为建立链数的标志。
9.4.4输出链表
首先按一个例子,编出一个输出链表的函数。
#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct student)//用sizeof 测量结构体的长度字节,让LEN恒等于这个字节。
struct student
{
long num;
float score;
struct student *next;
};//定义一个结构体。包含两个数据,和一个指针。
int n;
void print(struct student *head)//定义一个函数。形参定义为 struct student *head .输入一个结构体变量的地址。让他传递到这个函数。
{
struct student *p;//定义一个为结构体变量
printf("\nnow,these.%d records are :\n",n);//输出n
p=head;
if(head!=NULL)
{
do
{
printf("%ld %d5.1f\n",p->num,p->score);
p=p->next;
}while(p!=NULL);
}
}
整体将这个链表输出。直到遇到NULL为止。
也就是说在print 函数里面
do
{
printf("%ld %d5.1f\n",p->num,p->score);
p=p->next;
}while(p!=NULL);
这就是输出链表的关键,用do while 循环。也可以用while 循环。这里不需要计数,只要遇到NULL就行。所以用 do-while 和 while循环是比较好的。
我们把上面的一道例题和这一道立体综合起来。
/*建立一个动态链表,然后输出*/
#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct student)//
struct student
{
long num;
float score;
struct student*next;
};
int n;
struct student *creat(void)//建立一个动态链表
{
struct student*head;
struct student*p1,*p2;
n=0;
p1=p2=(struct student*)malloc(LEN);//建立一个动态储存区返回一个地址,这个动态储存区的长度为LEN;
scanf("%ld,%f",&p1->num,&p1->score);
head=NULL;
while(p1->num!=0)
{
n=n+1;
if(n==1)head=p1;
else p2->next=p1;
p2=p1;
p1=(struct student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
}
p2->next=NULL;
return(head);
}
void print(struct student*head)//建立一个输出链表的函数
{
struct student *p;
printf("\nnow,these.%d records are :\n",n);
p=head;
if(head!=NULL)
{
do
{
printf("%ld %5.1f\n",p->num,p->score);
p=p->next;
}while(p!=NULL);
}
}
int main()//主函数
{
struct student *head;
head=creat();
print(head);
return 0;
}
9.5 共用体类型
9.5.1 什么是共用体类型
前面我们所学习了结构体类型,结构体类型是将所有不同类型的数据联合在一起。
而我们现在学习的共用体类型,用同一段内存单元存放不同的类型的数据,是选择里面最长的一个数据,就拿这个最长的长度,作为共用体结构的长度。共用体是通过覆盖的实现的。
共用体的一般形式:
union 共用体名
{
成员列表
} 变量列表;
具体有如下三种定义形式:
union data
{
int i;
char ch;
float f;
}a,b,c;
///
union data
{
int i;
char ch;
float f;
};
union data a,b,c;
/这种定义的比较多
union
{
int i;
char ch;
float f;
}a,b,c;
和结构体一样,这个定义完只能有一次定义变量的机会,因为它没有定义变量名。
9.5.2 引用共用体变量的方式
引用公用体变量看看下面
a;错误
a.i;正确
a.ch;正确
a.f;正确
printf ("%d",a);错误
printf("%d",a.i);正确
首先咱们看一下,共用体虽然是一个综合的结构体,但是呢,他是具体表示某一个变量的。你如整型 浮点型。所以你不能通过共用体名字去引用。他不具体表示哪一个变量。只有a.i等才可以,因为是具体表示某一个变量的。再看结构体,结构体名字就是本身表示一个变量。所以说结构体可以引用,但是共用体不可以引用。
9.5.3共用体类型的特点
共用体类型有以下特点:
1)共用体可以存放不同类型的数据,但是在某一瞬间只能存放其中一个成员。
在某一瞬间,共用体只能存放某一种类型的数据;
2)可以初始化,但是只能放一个数值;
union data
{
int i;
char ch;
float f;
}a={1,'a',1.5};错误
union data a={16};正确
union data a={.ch='j'};正确,最好都加上.ch;之类的
3)共用体赋值时候直接按照那个数值的类型去储存,字节是类型中最大字节;
4)赋值后之前的数据会被覆盖,只有现在这个数据是有效的。
5)公用体变量的地址和他的成员的地址都是同一个地址。
&a.i=&a.c=&a.f;
&a的地址没有意义
6)变量名是不可以被赋值的,也不能被使用,需要具体到什么类型;
7)C99以后可以做函数参数(只提一下,以后学习中了解)
有若干人员的数据,其中包括学生和老师。学生的数据中包括,姓名 号码 性别 职业 班级 。
教师的数据包括:姓名,号码,性别 ,职业,职务,。要求制定一个表格来处理。
#include <stdio.h>
struct
{
int num;
char name[10];
char sex;
char job;
union
{
int clas;
char position[10];
}category;
}person[2];
int main()
{
int i;
for(i=0;i<2;i++)
{
printf("please enter the data of person:\n");
scanf("%d %s %c %c",&person[i].num,&person[i].name,&person[i].sex,&person[i].job);
if(person[i].job=='s')
scanf("%d",&person[i].category.clas);
else if(person[i].job=='t')
scanf("%d",&person[i].category.position);
else
printf("\n");
printf("No.name sex job class/position\n");
for(i=0;i<2;i++)
{
if(person[i].job=='s')
printf("%-6d%-10d%-4c%-10d\n",person[i].num,person[i].name,person[i].sex,person[i].job,person[i].category.clas);
else
printf("%-6d%-10d%-4c%-10d\n",person[i].num,person[i].name,person[i].sex,person[i].job,person[i].category.position);
}
}
return 0;
}
9.6 使用枚举类型
什么是枚举;就是自己去列举一些可能性,然后如果你定义一些变量;这些变量的取值,只能在这些可能性中取值。除非你改变枚举列表,否则你的变量不能取值在枚举之外的值。
怎么写一个枚举类型的结构,以及怎么去定义它
enum 枚举结构名
{
枚举表列(枚举常量)
}枚举变量;
如下:
enum weekday{sun,mon,tue,wed,thu,fri,sta};
enum weekday workday,weekend;
/
enum weekday{sun,mon,tue,wed,thu,fri,sta}workday,weekend;
/
enum {sun,mon,tue,wed,thu,fri,sta}workday,weekend;
也可以没有枚举结构名,但是相当于只能定义一次;
注意:
枚举元素是常量,他虽然也是由字符构成,看起来像变量,但是,他依旧是一个常量,所以他是不能够被直接赋值的。而且每个枚举元素都会被默认为一个整数;比如之前的sun就被默认0;mon就被默认1…以此类推。
printf("%d",sun);他是可以被输出的,输出的数值是0;
而且每个枚举元素也可以被设置为特定的数值;然后后面没有被赋值的,就顺推加1;
#include <stdio.h>
enum weekday{sun=7,mon=5,tue,wed,thu,fri,sta};
int main()
{
enum weekday a,b,c;
a=tue;
b=wed;
c=sta;
printf("%d %d %d",a,b,c);
return 0;
}
/*口袋中有红,黄,蓝,百,黑5种颜色的球若干个,每次从口袋里
先后取出三个球,问得到三种颜色球可能的取法,输出每种排列的情况*/
#include <stdio.h>
int main()
{
enum color{red,yellow,blue,white,black};
enum color i,j,k,pri;
int n,loop;//n 用来记录取法的可能。
n=0;
for(i=red;i<=black;i++)//i =red
for(j=red;j<=black;j++)//j=red yellow blue
if(i!=j)
{
for(k=red;k<=black;k++) //k=red yellow blue white black;
if((k!=i)&&(k!=j))
{
n=n+1; //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(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;
}
}
printf("\n");
}
}
printf("\ntotal:%5d\n",n);
return 0;
}
9.7 typedef声明新类型名
1.先按照定义变量的方法写出定义体(如int i)
2.将变量名换成新类型名(例如:将i换成count)
3.在最前面加typedef(例如:typedef int count)
4.然后就可以用新类型去定义变量;
数组:
int a[100];
int num[100];
typedef int num[100];
........
num a;
字符指针:
char *p;
char *string;
typedef char *string;
string p;
整型:
int i;
int num;
typedef int num;
num a;
struct
{
int a;
float b;
}a;
///
struct
{
...
}num;
///
typedef struct
{
...
} num;
num a;意思就是定义一个结构体变量a;
定义一个tyoedef ;就是将原有变量名用一个新的变量名去代替,新的变量名她具有原来旧的变量名一样的结构特点;typedef只是代替原来已有的变量名,不能够定义一个全新的,未知结构的变量类型;
他有什么好处呢?
首先他修改起来很方便。你可以修改定义,那么后面所有你所定义的类型变量你都直接都随之改变。使用方便。