今天来说一下C语言里的结构体(struct)、共用体(l联合体)union、枚举。
欢迎加入嵌入式学习群:559601187
(一)结构体:struct
1.1 概念
- 是一种自定义的数据类型
- 结构体是构造类型的一种
- 不同数据类型的集合
- 地址空间连续,每次分配最大数据类型的宽度
- 占用内存为所有变量的总大小(注意字节对齐问题)
1.2 定义
1.先定义结构体类型,再说明结构体变量
struct stu
{
char *name; //姓名
int num; //学号
int age; //年龄
float score; //成绩
};
struct stu student;
上面的程序使用strcuct关键字定义了一个结构体名为stu的结构体类型。和定义变量一样,声明一个结构体类型变量可以使用:数据类型 变量名 的形式。
struct stu student表示定义了一个变量名为stduent,类型为stu的结构体。该结构体含有4个成员:name、num、age、score
注意大括号后面的分号;不能少,这是一条完整的语句。
2.定义结构体类型的同时定义结构体变量
struct stu
{
char *name; //姓名
int num; //学号
int age; //年龄
float score; //成绩
}student;
在定义时直接声明结构体变量,只需要将结构体变量名放在花括号后面,并加上分号即可。
3.直接说明结构体变量
struct
{
char *name; //姓名
int num; //学号
int age; //年龄
float score; //成绩
} student;
这种定义方式并不常用,这样做书写虽然简单,但是因为没有结构体名,后面就没法用该结构体定义新的变量。
4.typedef重定义
typedef struct
{
char *name; //姓名
int num; //学号
int age; //年龄
float score; //成绩
} STU;
STU student;
这种方式比较常见,我们使用typedef重定义结构体为STU,这里STU就是此结构体类型,可以用STU去定义结构体变量
1.3 初始化
1.在定义结构体变量的时候全部初始化
struct stu
{
char *name; //姓名
int num; //学号
int age; //年龄
float score; //成绩
}student={“ha”,1234,56,99};
2.定义完结构体变量后,之后只能单个赋值
struct stu
{
char *name; //姓名
int num; //学号
int age; //年龄
float score; //成绩
};
struct stu student;
student.name="ha";
student.num=1234;
student.age=56;
student.score=99;
1.4 调用
结构体变量.成员
结构体变量名+点('.')+成员就可以调用了
1.5 结构体指针
当一个指针变量指向结构体时,我们就称它为结构体指针。C语言结构体指针的定义形式一般为:
struct 结构体名 *变量名;
//结构体
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
float score; //成绩
} stu1 = { "hah", 12, 18, 23, 136.5 };
//结构体指针
struct stu *pstu = &stu1;
注意结构体变量名和数组名不同,数组名在表达式中会被转换为数组指针,而结构体变量名不会,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址,必须在前面加&,所以给 pstu 赋值只能写作:
1.6 获取结构体成员
通过结构体指针可以获取结构体成员,一般形式为:
(*pointer).memberName
或者:
pointer->memberName
第一种写法中,.的优先级高于,(pointer)两边的括号不能少。如果去掉括号写作pointer.memberName,那么就等效于(pointer.memberName),这样意义就完全不对了。
第二种写法中,->是一个新的运算符,习惯称它为“箭头”,有了它,可以通过结构体指针直接取得结构体成员;这也是->在C语言中的唯一用途。
1.7 结构体内存分析
注意点
- 给整个结构体变量分配储存空间和数组一样,从内存地址比较大的开始分配
- 给结构体变量中的属性分配储存空间也和数组一样,从所占内存地址比较小的开始分配
- 定义结构体类型不会分配储存空间,只有定义结构体变量的时候才会分配储存空间
- 结构体在分配内存的时候,会做一个内存对齐的操作
- 会先获取所有属性中占用内存最大的属性的字节数
- 然后在开辟出最大属性字节的内存给第一个属性,如果分配给第一个属性之后还能继续分配给第二个属性,那么就继续分配给第二个属性
- 如果分配给第一个属性之后,剩余的内存不够分配给第二个属性了,那么会再次开辟最大属性的内存,再次分配 依次类推
#include
int main(){
//定义结构体
struct Person
{
char name; // 1 个节点 //开辟4个字节 char 占用1个
int age; // 4个字节 // 剩余三个 不够int 再开辟4个字节
int money; // 4个字节 // 再开辟4个字节
};
struct Person p; // 所以p = 4+4+4
printf("sizeof(p) = %i\n",sizeof(p)); // 12个字节
return 0;
}
(二)共用体(联合体):union
2.1概念
- 所有变量共用一段空间
- 每次分配按最大长度进行分配
- 是一种构造数据类型
- 同一时刻只能保存一个成员的值
- 不能直接引用共用体变量名
2.2定义
它的定义和结构体一样,分为先定义共用体再说明共用体变量、在定义共用体的同时说明共用体变量、直接说明共用体变量
union 共用体名{
成员列表
};
示例:
//先定义共用体再说明共用体变量
union data{
int n;
char ch;
double f;
};
union data a, b, c;
2.3 引用
和结构体一样,通过共用体变量名 . 成员名
union data{
int n;
char ch;
double f;
} a, b, c;
a.n=3;
关于共用体的详细介绍可以看下这篇文章C语言共用体(C语言union用法)详解,讲到共用体这里要说明一下大小端模式的问题。
小端模式:低地址存放低字节,高地址存放高字节
大端模式:低地址存放高字节,高地址存放低字节
(三)枚举:enum
3.1 概念
- 作用:列举出所有的可能性,增强代码的可阅读性
- 枚举成员都是常量
- 不能再对已经定义好的枚举常量赋值
3.1 定义
enum 枚举名
{
枚举变量
};
示例:
enum week
{
Mon, Tues, Wed, Thurs, Fri, Sat, Sun
};
typedef enum
{
Mon, Tues, Wed, Thurs, Fri, Sat, Sun
}Date;
枚举是一种类型,通过它可以定义枚举变量:
Date a,b,c
我们也可以给每个名字都指定一个值:
enum week{ Mon = 1, Tues = 2, Wed = 3, Thurs = 4, Fri = 5, Sat = 6, Sun = 7 };
更为简单的方法是只给第一个名字指定值:
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun };
这样枚举值就从 1 开始递增,跟上面的写法是等效的。
也可以在定义枚举类型的同时定义变量:
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a, b, c;
有了枚举变量,就可以把列表中的值赋给它:
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun };
enum week a = Mon, b = Wed, c = Sat;
或者:
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a = Mon, b = Wed, c = Sat;
特点:可以在定义枚举时给成员赋值,被赋值的成员往后依次增加1,也可以在中间改变某一个成员的值。
3.1 引用
直接使用就行,需要注意的是**枚举列表中的数据作用范围是全局的,不能在定义与它们名字相同的白能量;Mon、Tues、Wed 等都是常量,不能对它们赋值,只能将它们的值赋给其他的变量。
示例:
#include
int main(){
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } day;
scanf("%d", &day);
switch(day){
case 1: puts("Monday"); break;
case 2: puts("Tuesday"); break;
case 3: puts("Wednesday"); break;
case 4: puts("Thursday"); break;
case 5: puts("Friday"); break;
case 6: puts("Saturday"); break;
case 7: puts("Sunday"); break;
default: puts("Error!");
}
return 0;
}
Mon、Tues、Wed 这些名字都被替换成了对应的数字。这意味着,Mon、Tues、Wed 等都不是变量,它们不占用数据区(常量区、全局数据区、栈区和堆区)的内存,而是直接被编译到命令里面,放到代码区,所以不能用&取得它们的地址。这就是枚举的本质。
本文章仅供学习交流用禁止用作商业用途,文中内容来水枂编辑,如需转载请告知,谢谢合作
微信公众号:zhjj0729
微博:文艺to青年