结构体是什么?
- 结构体和数组一样属于构造类型
- 数组是用于保存一组相同类型数据的,而结构体是用于保存一组不同类型的数组
定义结构体
- 在使用结构体之前必须先定义结构体类型,因为C语言不知道你的结构体中需要存储哪些类型的数据,我们必须通过定义结构体类型来告诉C语言,我们的结构体中需要存储哪些类型的数据
- 格式
- struct 结构体名称{
数据类型 属性名称;
数据类型 属性名称;
... ...
};
- struct 结构体名称{
struct Person{
char *name;
int age;
double height;
};
// 注意:结尾有分号
定义结构体变量
- 格式: struct 结构体名 结构体变量名;
struct Person p;
// Person 结构体名
// p 结构体变量名
- 使用结构体变量
- 结构体变量名称.结构体属性名称;
p.name = "cww";
p.age = 35;
p.height = 1.9;
printf("name = %s\n", p.name);
printf("age = %i\n", p.age);
printf("height = %lf\n", p.height);
结构体变量初始化的几种方式
-
定义的同时按顺序初始化
struct Person{ char *name; int age; double height; }; struct Person p = {"cww", 18, 1.8};
-
定义的同时不按顺序初始化
struct Person{ char *name; int age; double height; }; struct Person p = {.age = 35, .name = "cww", .height = 1.8};
-
先定义后逐个初始化
struct Person{ char *name; int age; double height; } P; p.name = "cww"; p.age = 18; p.height = 1.8;
-
先定义后一次性初始化(不推荐使用)
struct Person { char *name; int age; double height; } P; P = (struct Person){"cww", 18, 1.8}
定义结构体变量的几种方式
- 先定义结构体类型,再定义结构体变量
struct Person{ char *name; int age; double height; }; struct Person p1;
- 定义结构体类型的同时定义结构体变量
struct Person{ char *name; int age; double height; } p2;
- 定义结构体类型的同时省略结构体名称,同时定义结构变量(匿名结构体)
- 特点: 结构体类型之能使用一次;
struct { char *name; int age; double height; } p3;
结构体类型作用域
- 结构类型定义在函数内部的作用域与局部变量的作用域是相同的
- 从定义的哪一行开始,直到遇到return或者大括号结束
#include <stdio.h> void test(); int main() { struct Person{ char *name; int age; double height; }; struct Person p; p.name = "cww"; p.age = 18; p.height = 1.8; printf("name = %s\n", p.name); test(); return 0; } void test(){ printf("name = %s\n", p.name); // 报错 }
- 结构类型定义在函数外部的作用域与全局变量的作用域是相同的
- 从定义的哪一行开始,知道文件结束为止
#include <stdio.h> struct Person{// 定义全局结构体 char *name; int age; double height; } ; void test(); int main() { struct Person p; p.name = "cww"; p.age = 18; p.height = 1.8; printf("name = %s\n", p.name); // cww test(); return 0; } void test(){ struct Person p2; //可以使用全局的结构体 p2.name = "ppp"; printf("name = %s\n", p2.name); // ppp }
结构体数组
- 结构体数组和普通数组并无太大差异,只不过是数组中的元素都是结构体而已
- 格式: struct 结构体类型名称 数组名称[元素个数];
struct Person {
char *name;
int age;
};
struct Person p[2];
- 结构体数组初始化和普通数组也一样,分为先定义后初始化和定义同时初始化;
- 定义的同时初始化
struct Person { char *name; int age; }; struct Person p[2] = {{"cww",18}, {"ppp", 20}};
- 先定义后初始化
```
struct Person {
char *name;
int age;
};
struct Person p[2];
p[0] = {"cww", 18};
p[1] = {"ppp", 20};
```
结构体内存分析
- 注意点:
- 给整个结构体变量分配存储空间和数组一样,从内存地址比较大的开始分配
- 给结构体变量中的属性分配存储空间也和数组一样,从所占用内存地址比较小的开始分配
- 注意点:
- 和数组不同的是,数组名保存的就是数组首元素的地址
- 而结构体变量名, 保存的不是结构体首属性的地址
#include <stdio.h> int main() { struct Person { int age; double height; }; struct Person p; // 结构体变量的名称并没有保存结构体首属性的地址; printf("p = %p\n", p); // 00401770 printf("&p = %p\n", &p); // 0028FEB0 return 0; }
- 结构体在分配内存的时候,会做一个内存对齐的操作(并不是都按最大属性字节分配)
- 会先获取所有属性中,占用内存最大的属性的字节
- 然后再开辟最大属性字节的内存给第一个属性
- 如果分配给一个属性之后还能继续分配给第二个属性,那么继续分配
- 如果分配给第一个属性之后,剩余的内存不够分配给第二个属性,那么会再次开辟最大属性字节的内存,再次分配
- 以此类推
```
struct Person{
int age; // 4
char ch; // 1
double score; // 8
};
struct Person p;
printf("sizeof = %i\n", sizeof(p)); // 16
```
- 调换顺序
```
struct Person{
int age; // 4
double score; // 8
char ch; // 1
};
struct Person p;
printf("sizeof = %i\n", sizeof(p)); // 24
```
结构体指针
因为结构体变量也会分配内存空间,所以结构体变量也有内存地址,所以也可以使用指针保存结构体变量的地址
-
规律:定义指向结构体变量的指针和过去定义指向普通变量的指针一样
struct Person { int age; double height; }; struct Person per = {22, 1.8}; struct Person *p; // p = per; // 错误写法, 写法和普通变量一样 p = &per; // 正确写法,要在变量前面添加取地址符号 &; printf("per = %p\n", &per); // 0028FEA8; printf("p = %p\n", p); // 0028FEA8;
-
如果指针指向了一个结构体变量,那么访问结构体变量的方式有3种
- 1.结构体变量名称.属性名称
- 2.(*结构体指针变量名称).属性名称;
- 3.结构体指针变量名称->属性名称;
1.结构体变量名称.属性名称 printf("per.age = %i\n", per.age); // 22 2.(*结构体指针变量名称).属性名称; printf("per.age = %i\n", (*p).age); // 22 3.结构体指针变量名称->属性名称; printf("per.name = %i\n", p->age); // 22
结构体嵌套定义
- 结构体的属性可以又是一个结构体
- 如果某个成员也是结构体变量,可以连续使用成员运算符 "." 访问低一级的成员
#include <stdio.h>
int main()
{
// 定义一个日期的结构体
struct Date{
int year;
int month;
int day;
};
// 定义一个人的结构体
struct Person{
char *name;
int age;
// 嵌套定义
struct Date birthday;
};
// 完全初始化
struct Person p = {"cww", 18, {2020,02,20}};
printf("name = %s\n", p.name); // cww
printf("year = %i\n", p.birthday.year); // 2020
printf("day = %i\n", p.birthday.day); // 20
return 0;
}
结构体和函数
- 结构体变量之间的赋值和基本数据类型一样,是值拷贝
#include <stdio.h>
int main()
{
struct Person{
char *name;
int age;
};
struct Person p1 = {"cww", 18};
struct Person p2;
p2 = p1;
p2.name = "ppp"; // 修改p2不会影响p1 是值拷贝
printf("p1.name = %s\n", p1.name); // cww
printf("p2.name = %s\n", p2.name); // ppp
return 0;
}
-
注意:
- 定义结构体类型不会分配存储空间
- 只有定义结构体变量才会分配存储空间;
结构体变量作为函数形参时也是值传递,在函数内修改形参,不会影响外界实参
#include <stdio.h>
struct Person{
char *name;
int age;
};
void test(struct Person p);
int main()
{
struct Person p1 = {"cww", 18};
printf("p1.name = %s\n", p1.name); // cww
test(p1);
printf("p1.name = %s\n", p1.name); // cww
return 0;
}
void test(struct Person p){
p.name = "hhh";
printf("p1.name = %s\n", p.name); // hhh
}
共用体
- 共用体和结构体不同的是,结构体的每个成员都是占用一块独立的存储空间,而共用体所有成员占用同一块存储空间
- 定义共用体格式:
- union 共用体名称{
数据类型 属性名称;
数据类型 属性名称;
... ...
};
- union 共用体名称{
union Test{
int age;
char ch;
};
- 定义共用体类型变量格式: union 共用体名 共用体变量名称
union Test t;
-
应用场景:
- 1.通信中的数据包会用到共用体
- 2.节约内存
- 3.需要大量的临时变量,这些变量类型不同
共用体定义的格式和结构体只有关键字不一样,结构体用struct,共用体用 union
-
共用体特点:
- 结构体的每个属性都会占用一块单独的存储空间,而共用体所有的属性都共用同一块存储空间
- 只要其中一个属性发生了改变, 其他的属性都会受到影响
#include <stdio.h>
int main()
{
union Test {
int age;
char ch;
};
union Test t;
printf("sizeof(t) = %i\n", sizeof(t)); //4
t.age = 18;
printf("t.age = %i\n", t.age); // 18
t.ch = 'a';
printf("t.ch = %c\n", t.ch); // a
printf("t.age = %i\n", t.age); // 97
return 0;
}
枚举
枚举用于提升代码的阅读性, 一般用于表示几个固定的值
应用场景: 如果某些变量的取值是固定的, 那么就可以考虑使用枚举来实现;
-
格式:
- enum 枚举类型名称{
取值1,
取值2,
};
- enum 枚举类型名称{
注意点: 和结构体不同,枚举是用逗号隔开
enum Gender{
male,
female,
};
-
规范:
- 枚举的取值一般是以K开头,后面跟上枚举类型名称,后面再跟上表达的含义
- K代表这是一个常量
- 枚举类型名称,主要是为了有多个枚举的时候,方便区分
- 含义,你想要定义名字
enum Gender{ KGenderMale, // 0 KGenderFemale, // 1 // 2 ... ... };
-
枚举的取值:
- 默认的情况下, 是从0开始取值,依次递增
- 也可以手动指定从几开始,依次递增;
enum Gender{ KGenderMale = 6, // 6 KGenderFemale, // 7 // 8 ... ... };
-
枚举类型的作用域
- 和结构体类型的作用域一样,和变量的作用域一样
#include <stdio.h> int main() { enum Gender { KGenderMale, KGenderFemale, }; printf("KGenderFemale = %i\n", KGenderFemale); // 1 return 0; }
#include <stdio.h> int main() { { enum Gender { KGenderMale, KGenderFemale, }; } printf("KGenderFemale = %i\n", KGenderFemale); // 报错 return 0; }
-
枚举类型变量的多种定义方式
- 和结构体类型的多种定义方式一样
// 先定义枚举类型,再定义枚举变量 enum Gender{ kGenderMale, KGenderFemale, } enum Gender g1; // 定义枚举类型的同时,定义枚举变量 enum Gender{ KGenderMale, KGenderFemale, } g2; // 定义枚举类型的同时,定义枚举变量,并且省略枚举类型名称 enum { KGenderMale, KGenderFemale, } g3;