1.初识结构体
1.为什么要用结构体
整型数,浮点型数,字符串是分散的数据表示
有时候我们需要用很多类型的数据来表示一个整体,比如学生信息
类比数组:数组是元素类型一样的数据集合
如果是元素类型不同的数据集合,就要用到结构体了
结构体的核心就是用不同变量的总体来描述一个事件
2.定义一个结构体
引入struct关键字,告知系统这是一个结构体
以上算是一个模板,一般不给赋具体的值,每一项在实际应用中并不是都要调用
3.成员列表
成员列表也称为域表,每个成员都是结构体中的一个域,一般称成员变量
4.初始化一个结构体并引用
5.注意事项
1.在声明的同时,定义变量,尽量少用
2.结构体没什么特殊的,只是把变量藏在结构体里面,而内部的变量和以前的东西是相同的道理,只是“触达的表示”不同
2.结构体数组
3.结构体指针
1.概念引入
回忆:
指针就是地址
指针变量就是存放地址的变量
结构体也是变量
变量访问有两种方式 : 1.变量名 2.地址
之前案例,是用变量名访问
通过结构体变量地址来访问该结构体
需要一个变量来保持这个地址:
这和之前说的指针,其实是一样的
只是指针类型是结构体
int a;
int *p;
p = &a;
struct Test t;
struct Test *p;
p = &t
点运算符来访问结构体中的成员变量(域)
#include <stdio.h>
#include <string.h>
struct Student{
int num;
char name[32];
char sex;
int age;
double score;
char addr[32];
};
int main(){
int a;
struct Student stu1;
stu1.num=1;
stu1.age=16;
stu1.score=98.5;
strcpy(stu1.name,"Bill");
strcpy(stu1.addr,"广东");
printf("学号:%d,年龄:%d,分数:%f,姓名:%s,地址:%s\n",
stu1.num,stu1.age,stu1.score,stu1.name,stu1.addr);
}
//学号:1,年龄:16,分数:98.500000,姓名:Bill,地址:广东
在上述示例中,还可将赋值过程进行优化,注意一一对应
struct Student stu2={2,"张三","男",17,98,"福建"};
printf("学号:%d,年龄:%d,分数:%f,姓名:%s,性别:%c,地址:%s\n",
stu2.num,stu2.age,stu2.score,stu2.name,stu2.sex,stu2.addr);
学号:2,年龄:17,分数:98.000000,姓名:张三,性别:g,地址:福建
2.通过结构体指针访问结构体
struct Test{
int idata;
char cdata;
};
int main(){
int a = 10;
int *p = &a;
char c ='c';
char *pc = &c;
struct Test t1 = {10,'c'}; //结构体整体赋值法
struct Test *ps = &tl;
printf("t1的idata=%d\n",tl.idata);//变量名访问,用点运算符
printf("t1的idata=%d\n",ps->idata);//指针访问,用"->"运算符
ps->cdata ='R';//指针访问,用"->"运算符
printf("t1的cdata=%c\n",t1.cdata);//变量名访问,用点运算符
printf("t1的cdata=%c\n",ps->cdata);//指针访问,用"->"运算符
return 0;
}
3.结构体数组,指针,函数应用
案例:选民投票系统
4.共用体/联合体
1.概念
1.有时候同一个内存空间存放类型不同,不同类型的变量共享一块空间
2.像结构体但是有区别
结构体元素有各自单独空间
共用体元素共享空间,空间大小由最大类型确定
struct TestT{
int idata;
char cdata;
double ddata;
};
union TestU{
int idata;
char cdata;
double ddata;
};
int main(){
struct TestT t1;
union TestU u1;
printf("结构体的大小是:%d\n",sizeof(t1));
printf("联合体的大小是:%d\n",sizeof(u1));
}
结构体的大小是:16
联合体的大小是:8,占用最大空间的域为double,8字节
struct TestT{
int idata;
char cdata;
int ddata;
};
union TestU{
int idata;
char cdata;
int ddata;
};
int main(){
struct TestT t1;
union TestU u1;
printf("结构体的大小是:%d\n",sizeof(t1));
printf("联合体的大小是:%d\n",sizeof(u1));
printf("idata:%p\n",&t1.idata);
printf("cdata:%p\n",&t1.cdata);
printf("ddata:%p\n",&t1.ddata);
printf("idata:%p\n",&u1.idata);
printf("cdata:%p\n",&u1.cdata);
printf("ddata:%p\n",&u1.ddata);
}
结构体的大小是:12
联合体的大小是:4,占用最大空间的域是int,大小为4字节
idata:000000000061FE14//结构体中成员变量的地址不同
cdata:000000000061FE18
ddata:000000000061FE1C
idata:000000000061FE10//共用体内成员变量的地址都一样
cdata:000000000061FE10
ddata:000000000061FE10
对比可以发现,联合体占用空间的大小取决于成员变量(域)占用的最大空间, 结构体占用空间的大小是成员变量占用空间的总和
结构体元素有各自单独空间,共用体元素共享空间
t1.idata=10;
t1.cdata='a';
t1.ddata=20;
printf("idata:%p,idata:%d\n",&t1.idata,t1.idata);
printf("cdata:%p,cdata:%d\n",&t1.cdata,t1.cdata);
printf("ddata:%p,ddata:%d\n",&t1.ddata,t1.ddata);
u1.idata=10;
u1.cdata='a';
u1.ddata=20;
printf("idata:%p,idata:%d\n",&u1.idata,u1.idata);
printf("cdata:%p,cdata:%d\n",&u1.cdata,u1.cdata);
printf("ddata:%p,ddata:%d\n",&u1.ddata,u1.ddata);
idata:000000000061FE14,idata:10
cdata:000000000061FE18,cdata:97
ddata:000000000061FE1C,ddata:20
idata:000000000061FE10,idata:20
cdata:000000000061FE10,cdata:20
ddata:000000000061FE10,ddata:20
对比发现,结构体元素互不影响, 共用体赋值会导致覆盖
3.注意数据覆盖
2.应用
案例:有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。要求用同一个表格来处理。
5.枚举类型
1.什么是枚举类型
如果一个变量只有几种可能的值,比如星期几 Sun Mon Tus Wed Thu Fri Sat
可以把所有的取值情况枚举完,故称枚举变量
2.定义枚举类型
列表中的名字可以自己定义,无需像变量一样去申请
C语言把当做常量处理,也称枚举变量
3.枚举变量
可以忽略枚举类型名,直接定义枚举变量
4.举例
第一个定义的方法
#include <stdio.h>
enum WeekDay {Sun,Mon=8,Tus,Wed,Thu,Fri,Sat};
int main(){
enum WeekDay w;//先定义一个枚举变量
enum WeekDay q;
// w=kkk;//非法赋值,只限列表中的集中情况
w=Sun;
q=Mon;
printf("w=%d\n",w);//w=0
printf("q=%d\n",q);//q=8;
return 0;
}
可以指定列表中枚举变量的值
值默认从0开始,枚举元素不能被赋值,虽然瞅着像变量名
不可非法赋值,赋值只限列表中的集中情况
枚举变量在不被赋值的情况下,输出的是其下标,注意下标从0开始
第二种定义的方法
enum WeekDay {Sun,Mon=8,Tus,Wed,Thu,Fri,Sat}w1,w2;
int main(){
w1=Sun;//忽略枚举类型名
w2=Mon;
printf("w1=%d\n",w1);//w=0
printf("w2=%d\n",w2);
return 0;
}
//w1=0;w2=8
第二种定义的方式和结构体、共用体的第二种定义是同样的道理
6.typedef关键字
1.给已有的变量类型起名字
2.一般配合结构体用,方便,不需要每次都用struct开头
C语言允许为一个数据类型起一个新的别名,就像给人起“绰号”一样。
起别名的目的不是为了提高程序运行效率,而是为了编码方便。
例如有一个结构体的名字是 stu,要想定义一个结构体变量就得这样写:
struct stu stu1;
struct 看起来就是多余的,但不写又会报错。如果为 struct stu 起一个别名STU,写起来就简单了:
STU stu1;
这种写法更加简练,意义也非常明确。
1.使用关键字 typedef可以为类型起一个新的别名。
typedef 的用法一般为:
typedef oldName newName;
oldName 是类型原来的名字,newName 是类型新的名字。 例如:
typedef int INTEGER;
INTEGER a, b;
a = 1;
b = 2;
INTEGER a, b;等效于int a, b;
2.typedef 还可以给数组、指针、结构体等类型定义别名。
一个给数组类型定义别名的例子:
typedef char ARRAY20[20];
表示 ARRAY20 是类型char [20]的别名。 它是一个长度为 20 的数组类型。接着可以用 ARRAY20 定义数组:
ARRAY20 a1, a2, s1, s2;
它等价于:
char a1[20], a2[20], s1[20], s2[20];
3.为结构体类型定义别名:
typedef struct stu{ char name[20]; int age; char sex;} STU;
STU 是 struct stu 的别名,可以用 STU 定义结构体变量:
STU body1,body2;
它等价于:
struct stu body1, body2;
4.为指针类型定义别名:
typedef int (*PTR_TO_ARR)[4];
表示 PTR_TO_ARR 是类型int * [4]的别名,它是一个二维数组指针类型。接着可以使用 PTR_TO_ARR 定义二维数组指针:
PTR_TO_ARR p1, p2;
5.为函数指针类型定义别名:
typedef int (*PTR_TO_FUNC)(int, int);
PTR_TO_FUNC pfunc;
举例:
#include <stdio.h>
typedef char (*PTR_TO_ARR)[30];
typedef int (*PTR_TO_FUNC)(int, int);
int max(int a, int b){
return a>b ? a : b;
}
char str[3][30] = {
"http://www.baidu.com",
"百度",
"Baidu"
};
int main(){
PTR_TO_ARR parr = str;
PTR_TO_FUNC pfunc = max;
int i;
printf("max: %d\n", (*pfunc)(10, 20));
for(i=0; i<3; i++){
printf("str[%d]: %s\n", i, *(parr+i));
}
return 0;
}
max: 20
str[0]: http://www.baidu.com
str[1]: 百度
str[2]: Baidu
案例
typedef int arr[10];//arr代替int [10]
struct Test{//结构体的第一种定义
int data;
int data2;
};
typedef struct{//结构体的第二种定义
int data;
int data1;
}Demo;
typedef struct Test T;//结构体的第三种定义(命名)
void printInfo(T t){
printf("%d\n",t.data);
}
int main(){
arr a ;//arr代替int [10]
a[0]=10;
printf("%d\n",a[0]);
struct Test t1;
t1.data=100;
printf("%d\n",t1.data);
Demo d;
d.data1=1000;
printf("%d\n",d.data1);
T t2;
t2.data=999;
printInfo(t2);
}
typedef和#define的区别
typedef 在表现上有时候类似于 #define,但它和宏替换之间存在一个关键性的区别。
把 typedef 看成一种彻底的“封装”类型,声明之后不能再往里面增加别的东西。
1.可以使用其他类型说明符对宏类型名进行扩展, 但对 typedef 所定义的类型名却不能这样做。如下所示:
#define INTERGE int
unsigned INTERGE n; //没问题
typedef int INTERGE;
unsigned INTERGE n; //错误,不能在 INTERGE 前面添加 unsigned
2.在连续定义几个变量的时候,typedef 能够保证定义的所有变量均为同一类型, 而 #define 则无法保证。例如:
#define PTR_INT int *
PTR_INT p1, p2;
经过宏替换以后,第二行变为:
int *p1, p2;
这使得p1、p2 成为不同的类型:p1 是指向 int 类型的指针,p2 是 int 类型 。
相反,在下面的代码中:
typedef int * PTR_INT
PTR_INT p1, p2;
p1、p2 类型相同,它们都是指向 int 类型的指针。