目录
编程练习:输入两个学生的名字,学号,成绩等输出成绩高的学生的信息
结构体引入:
为什么要引入结构体?
如果没有结构体的话就只能 int char float 单独去定义,不是一个有机的整体,利用结构体定义数据类型可以将这些数据都融合到一起,就可以模拟实现多一的数据类型。(类比数组,数组是相同的数据类型集合到一起,结构体是不同的数据类型集合到一起)
定义一个结构体:
编程练习:如何定义一个结构体
/*
如何定义一个结构体
*/
#include <stdio.h>
struct Student //告知系统是一个结构体,关键字"struct"
{
int num;
char name[32];
char sex[4];
char address[32];
int age;
double score;
}; //分号很重要,不能丢
struct Date
{
int year;
int month;
int day;
int hour;
int minute;
int seconds;
};
int main()
{
return 0;
}
编程练习:结构体变量的定义和访问
/*
结构体变量的定义和访问
*/
#include <stdio.h>
#include <string.h>
struct Student
{
int num;
char name[32];
char sex[4];
char address[32];
int age;
double score;
};
int main()
{
int a;
struct Student stu1; //struct Student就相当于int
struct Student stu2 = {20140678,"唔西迪兮","女","河北",18,97.5};
stu1.num = 20140656; //点运算符来访问结构体中的成员变量(域)
strcpy(stu1.name,"玛卡巴卡"); //给字符串赋值用字符串拷贝
strcpy(stu1.sex,"男");
strcpy(stu1.address,"山西");
stu1.age = 18;
stu1.score = 98.5;
printf("学号:%d 姓名:%s 性别:%s 地址:%s 年龄:%d 分数:%.1lf\n",
stu1.num,stu1.name,stu1.sex,stu1.address,stu1.age,stu1.score);
printf("学号:%d 姓名:%s 性别:%s 地址:%s 年龄:%d 分数:%.1lf\n",
stu2.num,stu2.name,stu2.sex,stu2.address,stu2.age,stu2.score);
return 0;
}
编程练习:输入两个学生的名字,学号,成绩等输出成绩高的学生的信息
/*
输入两个学生的名字,学号,成绩等输出成绩高的学生的信息
*/
#include <stdio.h>
struct Student
{
int num;
char name[32];
char sex[4];
char address[32];
int age;
double score;
};
int main()
{
struct Student stu1;
struct Student stu2;
struct Student Better;
printf("请输入学生1的学号、姓名、性别、地址、年龄、分数\n");
scanf("%d%s%s%s%d%lf",&stu1.num,stu1.name,stu1.sex,stu1.address,&stu1.age,&stu1.score);
printf("请输入学生2的学号、姓名、性别、地址、年龄、分数\n");
scanf("%d%s%s%s%d%lf",&stu2.num,stu2.name,stu2.sex,stu2.address,&stu2.age,&stu2.score);
printf("学生1的成绩:%.1lf 学生2的成绩:%.1lf\n",stu1.score,stu2.score);
Better = stu1;
if(stu1.score < stu2.score)
{
Better = stu2;
}
puts("成绩较好的是");
printf("num:%d name:%s sex:%s address:%s age:%d score:%.1lf\n",
Better.num,Better.name,Better.sex,Better.address,Better.age,Better.score);
return 0;
}
结构体数组:
它与前面讲的数值型数组几乎是一模一样的,只不过需要注意的是,结构体数组的每一个元素都是一个结构体类型的变量,都包含结构体中所有的成员项。
定义结构体数组的方法很简单,同定义结构体变量是一样的,只不过将变量改成数组。或者说同前面介绍的普通数组的定义是一模一样的,如:
struct Student stu[10];
这就定义了一个结构体数组,共有 10 个元素,每个元素都是一个结构体变量,都包含所有的结构体成员。
结构体数组的引用与引用一个结构体变量在原理上是一样的。只不过结构体数组中有多个结构体变量,我们只需利用 for 循环来遍历结构体数组中的元素。
编程练习:结构体数组应用
/*
结构体数组应用
*/
#include <stdio.h>
struct Student
{
int num;
char name[32];
char sex[4];
char address[32];
int age;
double score;
};
int main()
{
struct Student stu[3] = {
{20140656,"玛卡巴卡","男","山西",18,98.5},
{20140678,"唔西迪兮","女","河北",18,97.5},
{20140690,"依古比古","男","北京",18,95.5}
};
int i;
int len = sizeof(stu)/sizeof(stu[0]);
for(i=0;i<len;i++)
{
printf("num:%d name:%s sex:%s address:%s age:%d score:%.1lf\n",
stu[i].num,stu[i].name,stu[i].sex,stu[i].address,stu[i].age,stu[i].score);
}
return 0;
}
编程练习:选票系统
/*
选票系统
*/
#include <stdio.h>
#include <string.h>
struct Voter
{
int tickets;
char name[32];
};
int main()
{
struct Voter xm[3];
struct Voter Best;
int i;
int len = sizeof(xm)/sizeof(xm[0]);
int total = 6; //总票数为6
char tempname[32]; //定义一个临时名字来和选民的名字一一对比,相同的对应的票数加1
int j;
int mark = 0;
int abandon = 0;
//初始化环节
for(i=0;i<len;i++)
{
xm[i].tickets = 0;
printf("请输入第%d位选民的名字\n",i+1);
scanf("%s",xm[i].name);
}
//唱票环节
for(i=0;i<total;i++)
{
mark = 0;
printf("请问你想投给谁?\n");
memset(tempname,'\0',sizeof(tempname)); //每次投票时,临时名字都清空成0
scanf("%s",tempname);
for(j=0;j<len;j++)
{
if(strcmp(tempname,xm[j].name) == 0)
{ //将每次的投票和选民的名字进行对比,如果相同,对应的票数加1,如果不一样,则弃票
xm[j].tickets++;
mark = 1;
}
}
if(mark == 0) //如果投票和所有名字都不匹配,弃票加1
{
printf("弃票\n");
abandon++;
}
}
//公布环节
for(i=0;i<len;i++)
{
printf("name:%s tickets:%d\n",xm[i].name,xm[i].tickets);
}
putchar('\n');
Best = xm[0];
for(i=0;i<len;i++)
{
if(Best.tickets < xm[i].tickets)
{
Best = xm[i];
}
}
printf("经投票,%s以%d票当选,弃票%d票\n",Best.name,Best.tickets,abandon);
return 0;
}
结构体指针:
变量的访问有两种方式:1.变量名访问;2.通过地址来访问,结构体也一样,可以通过变量名和地址来访问结构体变量,因此引入结构体指针。
编程练习:结构体指针的定义及访问
/*
结构体指针的定义和应用
*/
#include <stdio.h>
struct Test
{
int indata;
char cdata;
};
int main()
{
int idata = 10;
int *pi = &idata; //之前定义指针的方法
char cdata = 'R';
char *pc = &cdata; //之前定义指针的方法
struct Test T1 = {20,'E'};
struct Test *pt = &T1;
printf("indata = %d,cdata = %c\n",T1.indata,T1.cdata);
//之前学到的用变量名访问结构体变量方法
printf("indata = %d,cdata = %c\n",pt->indata,pt->cdata);
//用结构体指针访问结构体变量的方法,用"->"取代点运算符
return 0;
}
编程练习:结构体指针的应用01
/*
用结构体指针来遍历数组
*/
#include <stdio.h>
struct Student
{
int num;
char name[32];
char sex[4];
char address[32];
int age;
double score;
};
int main()
{
struct Student stu[3] = {
{20140656,"玛卡巴卡","男","山西",18,98.5},
{20140678,"唔西迪兮","女","河北",18,97.5},
{20140690,"依古比古","男","北京",18,95.5}
};
struct Student *p;
p = stu;
int i;
int len = sizeof(stu)/sizeof(stu[0]);
for(i=0;i<len;i++)
{
printf("num:%d name:%s sex:%s address:%s age:%d score:%.1lf\n",
p->num,p->name,p->sex,p->address,p->age,p->score);
p++;
}
/* 使用指针遍历数组时,一次遍历指针会移到数组的尾巴,
所以当要再一次遍历时,要重新指向数组的首地址。
p = stu;
for(i=0;i<len;i++)
{
printf("num:%d name:%s sex:%s address:%s age:%d score:%.1lf\n",
p->num,p->name,p->sex,p->address,p->age,p->score);
p++;
}*/
return 0;
}
注意:如果使用结构体指针遍历数组后想要再次遍历数组的时候,要把指针再次指向数组首地址!!!要不然就会出现以下等问题:
编程练习:结构体指针应用02
/*
用结构体指针改写选票系统
*/
#include <stdio.h>
#include <string.h>
struct Voter
{
int tickets;
char name[32];
};
/*
1.把以前普通的变量名或者下标访问的运算符,改成结构体指针的 "->"
2.每次遍历数组都会指针都会偏移到数组尾巴,下次遍历之前要重新指向数组首地址
*/
int main()
{
struct Voter xm[3];
struct Voter Best;
struct Voter *p;
p = xm;
int i;
int len = sizeof(xm)/sizeof(xm[0]);
int total = 6; //总票数为6
char tempname[32]; //定义一个临时名字来和选民的名字一一对比,相同的对应的票数加1
int j;
int mark = 0;
int abandon = 0;
//初始化环节
for(i=0;i<len;i++)
{
p->tickets = 0;
printf("请输入第%d位选民的名字\n",i+1);
scanf("%s",p->name);
p++;
}
//唱票环节
p = xm;
for(i=0;i<total;i++)
{
mark = 0;
printf("请问你想投给谁?\n");
memset(tempname,'\0',sizeof(tempname)); //每次投票时,临时名字都清空成0
scanf("%s",tempname);
p = xm;
for(j=0;j<len;j++)
{
if(strcmp(tempname,p->name) == 0)
{ //将每次的投票和选民的名字进行对比,如果相同,对应的票数加1,如果不一样,则弃票
p->tickets++;
mark = 1;
}
p++;
}
if(mark == 0) //如果投票和所有名字都不匹配,弃票加1
{
printf("弃票\n");
abandon++;
}
}
//公布环节
p = xm;
for(i=0;i<len;i++)
{
printf("name:%s tickets:%d\n",p->name,p->tickets);
p++;
}
putchar('\n');
Best = xm[0];
p = xm;
for(i=0;i<len;i++)
{
if(Best.tickets < p->tickets)
{
Best = *p;
}
p++;
}
printf("经投票,%s以%d票当选,弃票%d票\n",Best.name,Best.tickets,abandon);
return 0;
}
编程练习:结构体指针数组函数综合应用改写选票系统
/*
结构体指针数组函数综合应用改写选票系统
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Voter
{
int tickets;
char name[32];
};
struct Voter *InitFun(struct Voter *p,int *len)
{
int i;
if(p == NULL)
{
printf("请输入有几个人参选?\n");
scanf("%d",len);
p = (struct Voter *)malloc(*len * sizeof(struct Voter));
}
for(i=0;i<*len;i++)
{
p->tickets = 0;
printf("请输入第%d位选民的名字\n",i+1);
scanf("%s",p->name);
p++;
}
return p-*len;
}
void PrintFun(struct Voter *p,int len)
{
int i;
for(i=0;i<len;i++)
{
printf("name:%s tickets:%d\n",p->name,p->tickets);
p++;
}
}
int CallFun(struct Voter *p,int len)
{
int i;
int total = 0;
char tempname[32];
int j;
int mark = 0;
int abandon = 0;
struct Voter *pback = p;
printf("请问总共有多少人投票?\n");
scanf("%d",&total);
for(i=0;i<total;i++)
{
mark = 0;
printf("请输入你想投给谁?\n");
memset(tempname,'\0',sizeof(tempname));
scanf("%s",tempname);
p = pback;
for(j=0;j<len;j++)
{
if(strcmp(tempname,p->name) == 0)
{
p->tickets++;
mark = 1;
}
p++;
}
if(mark == 0)
{
printf("弃票\n");
abandon++;
}
}
return abandon;
}
struct Voter *GetBest(struct Voter *p,int len)
{
int i;
struct Voter *Best;
Best = p;
for(i=0;i<len;i++)
{
if(Best->tickets < p->tickets)
{
Best = p;
}
}
return Best;
}
int main()
{
struct Voter *xm = NULL;
int len = 0;
xm = InitFun(xm,&len);
PrintFun(xm,len);
int abandon = CallFun(xm,len);
PrintFun(xm,len);
struct Voter *Best = GetBest(xm,len);
printf("%s以%d票当选,弃票%d票\n",Best->name,Best->tickets,abandon);
return 0;
}
结构体二级指针:
假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,它们的关系如下图所示:
将这种关系转换为C语言代码:
- int a =100;
- int *p1 = &a;
- int **p2 = &p1;
指针变量也是一种变量,也会占用存储空间,也可以使用&
获取它的地址。C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*
。p1 是一级指针,指向普通类型的数据,定义时有一个*
;p2 是二级指针,指向一级指针 p1,定义时有两个*
。(结构体的二级指针也一样)
编程练习:结构体二级指针
/*
结构体二级指针应用
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Voter
{
int tickets;
char name[32];
};
struct Voter *InitFun(struct Voter **p,int *len)
{
int i;
if(*p == NULL)
{
printf("请输入有几个人参选?\n");
scanf("%d",len);
*p = (struct Voter *)malloc(*len * sizeof(struct Voter));
}
for(i=0;i<*len;i++)
{
(*p)->tickets = 0; //当二级指针的"*"和"->"运算符在一起的时候要括起来注意符号优先级问题
printf("请输入第%d位选民的名字\n",i+1);
scanf("%s",(*p)->name);
(*p)++;
}
return *p - *len;
}
void PrintFun(struct Voter *p,int len)
{
int i;
for(i=0;i<len;i++)
{
printf("name:%s tickets:%d\n",p->name,p->tickets);
p++;
}
}
int CallFun(struct Voter *p,int len)
{
int i;
int total = 0;
char tempname[32];
int j;
int mark = 0;
int abandon = 0;
struct Voter *pback = p;
printf("请问总共有多少人投票?\n");
scanf("%d",&total);
for(i=0;i<total;i++)
{
mark = 0;
printf("请输入你想投给谁?\n");
memset(tempname,'\0',sizeof(tempname));
scanf("%s",tempname);
p = pback;
for(j=0;j<len;j++)
{
if(strcmp(tempname,p->name) == 0)
{
p->tickets++;
mark = 1;
}
p++;
}
if(mark == 0)
{
printf("弃票\n");
abandon++;
}
}
return abandon;
}
struct Voter *GetBest(struct Voter *p,int len)
{
int i;
struct Voter *Best;
Best = p;
for(i=0;i<len;i++)
{
if(Best->tickets < p->tickets)
{
Best = p;
}
}
return Best;
}
int main()
{
struct Voter *xm = NULL;
int len = 0;
xm = InitFun(&xm,&len);
PrintFun(xm,len);
int abandon = CallFun(xm,len);
PrintFun(xm,len);
struct Voter *Best = GetBest(xm,len);
printf("%s以%d票当选,弃票%d票\n",Best->name,Best->tickets,abandon);
return 0;
}
共用体/联合体引入:
共用体和结构体十分相似,是一种构造类型或复杂类型,它也可以包含多个类型不同的成员。
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
注意:结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
编程练习:共用体定义和使用
/*
共用体的定义及其注意事项
*/
#include <stdio.h>
struct TestT
{
int indata;
char cdata;
double ddata;
};
union TestU
{
int indata;
char cdata;
};
int main()
{
struct TestT t1;
union TestU u1;
t1.indata = 10;
t1.cdata = 'c';
t1.ddata = 98.5;
u1.indata = 10;
u1.cdata = 'd';
printf("结构体大小是sizeof = %d\n",sizeof(t1)); //内存大于或等于所有成员的内存
printf("共用体大小是sizeof = %d\n",sizeof(u1)); //内存大小由最大成员的内存决定
printf("结构体:\n indata:%p %d\n cdata:%p %d\n ddata:%p %d\n",t1.indata,t1.cdata,t1.ddata);
//结构体成员的每一个都有单独的地址
printf("共用体:\n indata:%p %d\n cdata:%p\n ",u1.indata,u1.cdata);
//共用体所有成员都共用一个地址
//因为结构体成员每一个都有独立的内存空间,所以它的每一个成员都可以打印出来。互不干扰;
//因为共用体成员每一个都是公用同一个内存空间,所以它的值会被之后一个输入的成员覆盖掉。
return 0;
}
编程练习:共用体开发案例
/*
共用体应用
*/
#include <stdio.h>
struct Person
{
char name[32];
int num;
char sex[4];
char Profession;
union
{
char class[8];
char subject[4];
}mes;
};
int main()
{
struct Person p[2];
int i;
int len = sizeof(p)/sizeof(p[0]);
for(i=0;i<len;i++)
{
printf("请输入您的职业,t or s\n");
scanf("%c",&(p[i].Profession));
if(p[i].Profession == 't')
{
printf("请老师输入名字\n");
scanf("%s",p[i].name);
printf("请老师输入所教的科目\n");
scanf("%s",p[i].mes.subject);
}
else
{
printf("请学生输入名字\n");
scanf("%s",p[i].name);
printf("请学生输入所在班级\n");
scanf("%s",p[i].mes.class);
}
getchar();
}
for(i=0;i<len;i++)
{
if(p[i].Profession == 't')
{
printf("老师的名字是%s,所教的科目是%s\n",p[i].name,p[i].mes.subject);
}
else
{
printf("学生的名字是%s,所在班级是%s\n",p[i].name,p[i].mes.class);
}
}
return 0;
}
枚举类型
在实际编程中,有些数据的取值往往是有限的,只能是非常少量的整数,并且最好为每个值都取一个名字,以方便在后续代码中使用,比如一个星期只有七天,一年只有十二个月,一个班每周有六门课程,这时候就要用到枚举类型。
编程练习:如何定义枚举类型且使用
/*
枚举类型
*/
#include <stdio.h>
enum Month
{
Jan = 1,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec
}m1,m2;
int main()
{
m1 = Feb;
m2 = Dec;
if(m1 < m2)
{
printf("我看你是在为难我玛卡巴卡\n");
}
printf("m1 = %d m2 = %d\n",m1,m2);
return 0;
}
/*
1.值默认从0开始,枚举元素不能被赋值,虽然瞅着象变量名
2.可以指定列表中枚举数的值
3.可以直接忽略枚举类型名,直接定义枚举变量
*/
typedef 关键字
C语言允许用户使用 typedef 关键字来定义自己习惯的数据类型名称,来替代系统默认的基本类型名称、数组类型名称、指针类型名称与用户自定义的结构型名称、共用型名称、枚举型名称等。一旦用户在程序中定义了自己的数据类型名称,就可以在该程序中用自己的数据类型名称来定义变量的类型、数组的类型、指针变量的类型与函数的类型等。(方便用户使用)
编程练习:typedef 使用
#include <stdio.h>
typedef struct
{
int num;
char name[32];
char sex;
}S,*p;
void print1(S p)
{
printf("num:%d name:%s sex:%c\n",p.num,p.name,p.sex);
}
void print2(S *p)
{
printf("num:%d name:%s sex:%c\n",p->num,p->name,p->sex);
}
int main()
{
S p1 = {20140656,"玛卡巴卡",'b'};
S p2 = {20140678,"唔西迪兮",'g'};
print1(p1);
print1(p2);
S *pp1 = &p1;
S *pp2 = &p2;
print2(&p1);
print2(&p2);
return 0;
}