【C语言】自定义类型:结构体
目录
一.结构体的声明
1.结构的声明
struct stu {
char name[20];
int age;
char sex[5];
char id[20]
};
2.结构体变量的创建和初始化
#include <stdio.h>
struct Stu {
char name[20];
int age;
char sex[5];
char id[20]
};
int main() {
struct Stu s1 = { "zhangsan",20,"nan","20211111" };
struct Stu s2 = {.age = 20,.name = "lisi",.id = 20231111,.sex = "nan"};
return 0;
}
3.结构的特殊声明
匿名结构体类型
struct {
int a;
char b;
float c;
}x;
匿名的结构体类型结构,如果没有对结构体类型重命名,则在结构体后创建的x只能使用一次
4.结构体的自引用
struct node {
int data;
struct node* next;
};
二.结构体内存对齐(计算结构体大小)
1.对齐规则
①结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
②从第二个成员变量开始,都要对齐到对齐数的整数倍处
对齐数 = 编译器默认的一个对齐数和该成员变量大小的较小值
vs中默认对齐数为8,gcc中没有默认对齐数
③结构体的总大小为最大对齐数的整数倍
④如果嵌套了结构体,嵌套的成员对齐到自己成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数
2.修改默认对齐数
#pragma pake(1); :设置默认对齐数为1
#pragma pake(); :取消设置的对齐数,还原为默认
3.结构体传参
#include<stdio.h>
struct S {
int data[100];
int num;
};
struct S s = { {1,2,3,},100 };
void My_printf(struct S* ps){
printf("%d\n", ps->num);
}
int main() {
My_printf(&s);
return 0;
}
结构体传参时,要传递结构体的地址
三.结构体实现位段
1.位段
位段(Bit Field)是结构体中一种特殊的成员,允许以位为单位指定成员所占的内存空间。这种机制常用于节省存储空间或直接访问硬件寄存器中的特定位。
struct 结构体名 {
数据类型 成员名 : 位宽;
};
数据类型:通常为 int、unsigned int 或 _Bool(C99 引入)。其他类型可能因编译器而异。
位宽:指定成员占用的位数,必须为非负整数且不超过数据类型本身的位数。
成员名后面加上冒号和数字。
例如:
struct A {
int _a : 2;
int _b : 5;
int c : 10;
};
2.位段的内存分配
①位段的类型可以是int,unsigned int,signed int,char类型。
②位段的空间上是按照需要,以四个字节(int)或一个字节(char)的方式来开辟的。
③位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段,
例如:
#include<stdio.h>
struct S {
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main() {
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
开辟空间演示:

在vs中,一个字节的内部,内存是从右向左使用的(避免剩余空间的浪费)。
先在低地址处创建一个字节内存大小,在内存内部中的右侧开辟a的空间(三个比特位),由于在第一次开辟的一个字节内存中还有剩余空间并且大小能够被b(四个比特位)使用,因此在a 的右边存放b。c(五个比特位),第一次开辟的空间不够,因此在较高地址处新开辟一个字节的空间,用来存放c;以此类推。
3,位段的注意事项
-
内存对齐:位段成员的实际布局受编译器对齐规则影响,不同平台可能有差异。
-
跨平台兼容性:位段的存储顺序(高位优先或低位优先)由编译器决定,涉及跨平台时需谨慎。
-
取地址操作:无法对位段成员使用取地址运算符
&,因为其可能不按字节边界对齐
下面代码演示的是取地址操作的注意事项:
由于位段成员可能共用一个字节,这样有些成员的起始位置不是该成员的起始位置 ,那么这些位置处是没有位置的。(内存中每个字节分配一个地址,一个字节的内部bit位是没有地址的)
所有不能对位段成员使用&操作符,这样就不能使用scanf直接给位段的成员输入值,只能先输入放在一个变量中,然后赋值给位段的成员。
#include<stdio.h>
struct S {
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 20;
};
int main() {
struct S s = { 0 };
//scanf("%d",&s.b);这是错误的写法
//正确写法
int b = 0;
scanf("%d", &b);
s._b = b;
return 0;
}
4.实际应用场景
- 硬件寄存器映射:直接操作硬件寄存器中的特定位(如状态标志)。
- 协议解析:紧凑存储协议头部的标志位(如 TCP 头部中的控制位)。
- 资源受限环境:嵌入式系统中节省内存空间。
拓展:
网络协议中的典型应用
- IP协议头:IP头中的版本号(4位)、头部长度(4位)、服务类型(8位)等字段使用位段实现。
- TCP协议头:TCP头中的控制标志(如SYN、ACK、FIN等)通常占用1位,组合成6位的标志字段。
- 自定义协议:私有协议中常用位段压缩多个状态标志,减少传输开销。
完
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)