一、结构体的定义:自定义数据类型
结构体是一种构造数据类型,我们可以根据需求定义包含多个不同类型成员的新类型。
语法模板
struct 类型名
{
数据类型 成员变量名1;
数据类型 成员变量名2;
数据类型 成员变量名3;
// ... 更多成员
};
// 定义日期结构体
struct date
{
int year;
int mon;
int day;
};
// 定义时间结构体
struct time
{
int hour;
int min;
int sec;
};
// 定义学生结构体
struct Stu
{
char name[50]; // 姓名
int age; // 年龄
char sex; // 性别
int score; // 成绩
};
关键点:结构体定义只是创建了一个 “数据模板”,不会分配内存,只有定义变量时才会占用内存。
二、定义结构体变量:基于模板创建实例
定义结构体变量的方式和普通变量类似,只是需要带上struct关键字和结构体名:
// 普通变量定义
int a;
// 结构体变量定义
struct 结构体名 变量名;
struct Stu zhangsan; // 定义一个学生变量zhangsan
三、结构体初始化:给变量赋初始值
结构体支持多种初始化方式,根据需求选择即可:
1. 全部初始化
按成员顺序一次性赋值:
struct Stu s = {"zhangsan",20,'m',90};
2. 局部初始化(GNU GCC 编译器支持)
可以只给部分成员赋值,未赋值的成员会被初始化为 0:
struct Stu s2 = {
.name="zhangsan",
.score = 80,
};
3. 初始化为全 0
快速将所有成员置为 0:
struct Stu s3={0};
四、结构体成员的访问:两种方式
访问结构体成员有两种常用写法,取决于你是直接操作变量还是通过指针操作。
1. 变量访问:使用 . 运算符
struct Stu s = {"zhangsan",20,'m',90};
s.age = 22; // 直接修改年龄
strcpy(s.name,"zhang123"); // 字符串赋值需要用strcpy,不能直接赋值数组名
printf("name:%s age:%d sex:%c score:%d\n",s.name,s.age,s.sex,s.score);
2. 指针访问:使用 -> 运算符
struct Stu *p = &s; // 定义指向结构体的指针
printf("name:%s age:%d sex:%c score:%d\n",p->name,p->age,p->sex,p->score);
五、结构体的存储:字节对齐问题
结构体的内存布局不是简单的成员大小相加,而是会遵循字节对齐规则,这是为了让 CPU 更高效地访问数据。
对齐规则
- 成员对齐:结构体的每个成员必须存放在 “自身类型大小的整数倍” 地址上。例如:
int类型成员的地址必须是 4 的倍数,double类型成员的地址必须是 8 的倍数。 - 整体对齐:结构体的总大小必须是 “最大成员类型大小” 的整数倍。
示例:计算结构体大小
struct Stu
{
char name[30]; // 30字节
int age; // 4字节
short ID; // 2字节
double cost; // 8字节
};
- 理论大小:
30+4+2+8=44 - 实际大小:由于字节对齐,最终大小为
48(最大成员是double,大小 8,48 是 8 的整数倍)。
理解对齐:编译器会在成员之间填充空白字节,虽然浪费了空间,但提升了 CPU 的访问效率。
六、结构体参数的传递:推荐用指针
结构体作为函数参数时,有两种传递方式:
1. 值传递(不推荐)
会拷贝整个结构体,当结构体较大时,效率很低且浪费内存。
2. 地址传递(推荐)
传递结构体的指针,仅拷贝 8 字节(64 位系统)的地址,效率更高,且可以在函数内修改原结构体的内容。
实战示例
// 输入学生信息:用指针传递,修改原结构体
void input_stu(struct Stu* p)
{
printf("input name:");
gets(p->name);
printf("input age:");
scanf("%d",&p->age);
getchar(); // 清除输入缓冲区的换行
printf("input sex:");
scanf("%c",&p->sex);
printf("input score:");
scanf("%lf",&p->score);
}
// 展示学生信息:用const修饰指针,保证不修改结构体
void show_stu(const struct Stu* p)
{
printf("name:%s age:%d sex:%c score:%lf\n",p->name,p->age,p->sex,p->score);
}
int main()
{
struct Stu s;
input_stu(&s); // 传递地址
show_stu(&s);
return 0;
}
七、结构体数组:批量管理结构体变量
当需要管理多个结构体实例(如多个学生)时,结构体数组是最佳选择。
定义与初始化
struct Stu a2[3]={
{"zhangsan",1.77},
{"lisi",1.65},
{"wangmazi",1.22}
};
// 局部初始化(GNU GCC支持)
struct Stu a3[3]={
[1]={
.name="zhangfei",
}
};
数组元素的操作
// 结构体变量之间可以直接复制
a[1] = a[0];
// 遍历数组
int i = 0 ;
for(i=0;i<3;i++)
{
printf("id:%d name:%s height:%lf\n",i,a2[i].name,a2[i].heigh);
}
八、共用体(Union):节省内存的共享存储
共用体是一种为了节省内存而设计的自定义结构,也叫联合体,它的核心特点是所有成员共享同一块内存空间。
1. 核心特性
- 内存共享:结构体的成员内存是独立的,而共用体的所有成员共用同一块内存,修改一个成员会覆盖其他成员的值。
- 节省内存:共用体的大小等于最大成员的大小,在早期内存紧张的计算机上非常实用。
- 函数参数传递:与结构体类似,推荐使用指针传递,避免拷贝开销。
2. 定义语法
union 共用体名字
{
数据类型 成员变量1;
数据类型 成员变量2;
数据类型 成员变量3;
// ... 更多成员
};
3. 字节序关联知识点
在使用共用体处理多字节数据时,需要注意 CPU 的字节序:
- 大端存储(如 51 单片机、网络协议):数据的低位存放在内存的高地址,高位存放在低地址。
- 小端存储(如 ARM、Intel 处理器):数据的低位存放在内存的低地址,高位存放在高地址。
九、枚举类型(Enum):限定取值范围的常量集合
枚举是将变量的取值范围一一列举出来,变量只能在这个范围内取值,它本质是整型(int)常量的集合。
1. 核心特性
- 常量集合:枚举出来的值是编译期确定的常量,不能在运行时修改。
- 默认值规则:枚举值默认从 0 开始,后一个值在前一个值的基础上 + 1;也可以手动指定起始值。
2. 定义与使用
// 定义枚举类型:一周的星期
enum WEEK {MON,TUE,WED,THU,FRI,SAT,SUN}; // 默认MON=0,TUE=1...SUN=6
enum WEEK {MON=2,TUE,WED,THU,FRI,SAT,SUN}; // 手动指定MON=2,后续值依次+1
int main()
{
enum WEEK w = FRI; // 定义并初始化枚举变量
w = SUN; // 变量w的本质是int类型,可以赋值范围内的枚举值
// 枚举变量非常适合用于switch分支
switch(w)
{
case MON:
case TUE:
case WED:
printf("好好学习\n");
break;
case THU:
case FRI:
case SAT:
printf("放飞自我\n");
break;
case SUN:
printf("放飞自我*2\n");
break;
}
// 注意:C语言不强制检查枚举值范围,赋值超出范围不会报错,但逻辑上存在风险
w = 100; // GCC不会报警告,但这是一个潜在错误
return 0;
}
十、位运算:直接操作二进制位的高效工具
位运算是直接对数据的二进制位进行操作,它的效率极高,常用于底层驱动、加密算法和性能优化场景。
1. 基础位运算符
| 运算符 | 含义 | 备注 | |
|---|---|---|---|
& | 按位与:对应位都为 1 时结果为 1 | 双目 | |
| ` | ` | 按位或:对应位有一个为 1 时结果为 1 | 双目 |
^ | 按位异或:对应位不同时结果为 1 | 双目 | |
~ | 按位取反:0 变 1,1 变 0 | 单目 | |
<< | 逻辑左移:高位丢弃,低位补 0 | 双目 | |
>> | 逻辑右移:低位丢弃,高位补 0 | 双目 |
2. 核心特点
- 操作对象:仅支持整型数据(
char、int、short、long等)。 - 高效性:位运算直接在 CPU 寄存器中完成,比算术运算快得多。

1488

被折叠的 条评论
为什么被折叠?



