news 2026/10/31 2:13:48

C语言结构体:把一堆不同类型的数据打包在一起[特殊字符]

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言结构体:把一堆不同类型的数据打包在一起[特殊字符]

写 C 语言的时候,char、int、float 这些都是基础类型。但现实世界中的信息往往是成套出现的:一个学生,有姓名、年龄、性别、学号;一本书,有书名、作者、价格。总不能单独定义一堆零散变量name1、age1、sex1……,写起来乱糟糟,还容易搞混。结构体(struct)就是专门用来把多种不同类型的数据打包到一起的自定义类型。

一、结构体怎么写:声明与初始化

先来看结构体的语法模板:

struct 结构体标签名 { 成员1; 成员2; 成员3; }; //⚠️最后的分号千万不能丢!

举个例子,用结构体描述一名学生:

struct Stu { char name[20]; //名字 int age; //年龄 char sex[5]; //性别 char id[20]; //学号 };

定义变量和初始化有两种方式:

  1. 按成员顺序初始化
struct Stu s = {"张三", 20, "男", "202601001"};
  1. 指定成员初始化,顺序可以随便写(C99 支持)
struct Stu s2 = {.age=18, .name="李四", .sex="女", .id="202601002"};

访问结构体成员时,普通变量用.点操作符;如果是结构体指针,则用->箭头操作符。

printf("%s\n", s.name); //结构体变量 . 成员 struct Stu* ps = &s; printf("%d\n", ps->age); //结构体指针 -> 成员

匿名结构体:用完即走

声明时不写标签名,就是匿名结构体。

struct { int a; char b; float c; } x;

⚠️坑:两个长得一模一样的匿名结构体,编译器会当成完全不同的两种类型,既不能互相赋值,指针也不能互相指向。匿名结构体一般只用一次,不适合复用。

结构体自引用:写链表必会!

很多同学写链表时会犯下面这个错误:

//错误写法! struct Node { int data; struct Node next; //结构体里面放一个完整自己,大小无限递归 };

结构体内部不能放自身类型的完整变量,否则大小根本算不出来。✅正确做法是存放结构体指针,因为指针大小是固定的。

struct Node { int data; struct Node* next; };

用 typedef 重命名时也要注意:不要用匿名结构体做自引用,此时 typedef 还没生效,结构体内部识别不到新名字。

//不行!Node还没定义出来 typedef struct { int data; Node* next; } Node; //标准正确写法 typedef struct Node { int data; struct Node* next; } Node;

二、重点难点:结构体内存对齐(笔试高频)

问:下面两个结构体,成员完全一样,sizeof大小却不一样?

struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; int i; };

在 VS 下:sizeof(S1)=12,sizeof(S2)=8。成员一模一样,只是顺序不同,占用的内存大小就不一样,这就是内存对齐在起作用。

四条对齐规则(记牢做题无敌)

  1. 结构体第一个成员,放在偏移为 0 的地址。
  2. 后面每一个成员,都要对齐到对齐数的整数倍地址。

    对齐数 =编译器默认对齐数和该成员自身大小中较小的那一个。VS 默认对齐数是 8;Linux GCC 没有默认对齐数,对齐数就等于成员自身大小。

  3. 结构体总大小必须是结构体“最大对齐数”的整数倍,不够就补填充字节(空洞)。
  4. 如果结构体里面嵌套了另一个结构体,嵌套的结构体要对齐到它自身最大对齐数的整数倍;整体大小则是全部最大对齐数的整数倍。

演算 S1:char c1; int i; char c2;

  • c1:char 占 1 字节,偏移 0。
  • i:int 大小 4,对齐数 min(8,4)=4,要放在 4 的倍数地址上。0 地址已被占用,中间补 3 个空洞,i 放在偏移 4。
  • c2:char 占 1 字节,放在偏移 8。此时成员占用到 9 字节。最大对齐数是 4,总大小必须是 4 的倍数,9 向上取整到 12,所以sizeof(S1)=12。

S2:char c1; char c2; int i;

  • c1 偏移 0,c2 偏移 1;
  • i 对齐数 4,放到偏移 4;占用到 7 字节;向上对齐到 8,所以sizeof(S2)=8。

💡小技巧:写结构体想省内存,就把占用字节小的成员尽量放在一起,减少填充空洞。

修改默认对齐数#pragma pack

我们也可以手动修改编译器的默认对齐数。

#pragma pack(1) //设置默认对齐数为1 struct S { char c1; int i; char c2; }; #pragma pack() //取消设置,恢复编译器默认

pack(1) 的时候,对齐数全部为 1,不再有空洞,上面这个结构体的大小就是 6。

一般业务代码不建议随便改 pack,只有驱动、协议解析这类场景才会用到。

为什么要有内存对齐?(拿空间换时间)

  1. 硬件平台限制:有些 CPU 硬件不支持从任意地址读取 4 字节 int,地址不对齐会直接触发硬件报错。
  2. 访问性能提升:如果数据没对齐,CPU 需要分两次内存读取再拼接数据;对齐之后一次就能读完。

简单讲:牺牲一点点空闲的填充字节,换取读取速度和硬件兼容性。

三、结构体传参:尽量传地址,不要传整个结构体!

struct S { int arr[1000]; int num; }; //直接传整个结构体(不推荐) void print1(struct S s) { printf("%d", s.num); } //传结构体的地址(推荐) void print2(struct S* ps) { printf("%d", ps->num); }

调用方式如下:

struct S s; print1(s); //把整个结构体拷贝一份压入栈,如果结构体很大,栈开销巨大 print2(&s); //仅仅压入一个8字节/4字节指针,开销极小

📌结论:结构体传参优先传递指针。如果函数内部不想修改原结构体,可以写成const struct S* ps,保护内容不被修改。

四、结构体延伸:位段(bit-field)

位段是结构体的特殊玩法,可以直接按 bit 位分配空间。冒号后面写数字,表示这个成员占用多少 bit。

struct A { int _a :2; int _b :5; int _c :10; int _d :30; };

位段要点

  1. 位段成员必须是整型家族:char、int、unsigned int。
  2. 空间开辟:按int(4字节)或者char(1字节)为单位开辟内存。能塞就塞,塞不下就新开一块空间。
  3. ❗位段跨平台,不具备可移植性!
  • 到底是从高位往低位放 bit,还是从低位往高位放,C 标准没有强制规定,VS 和 GCC 的行为并不一样。
  • 剩余的 bit 是丢弃还是复用,标准同样未定义。

    位段适合做网络协议、硬件寄存器解析;追求跨平台的业务代码尽量避开位段。

还有一个坑:bit 位没有内存地址,不能取地址 &

struct A sa; scanf("%d", &sa._b); //❌错误,不能对位段取地址

正确做法是:先读到临时变量,再赋值给位段成员。

int temp; scanf("%d", &temp); sa._b = temp;

📖本篇快速总结

  1. struct 结构体可以把不同类型的成员打包在一起,用.访问成员,指针则用->。
  2. 结构体自引用只能放同类型指针,不能放完整的结构体变量。
  3. 内存对齐 4 条规则是计算 sizeof 结构体的核心;小成员放在一起可以节省内存。
  4. 结构体传参优先传指针,减少栈拷贝开销。
  5. 位段可以按 bit 使用内存、节省空间,但跨平台行为不确定,也不能用 & 取地址。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/31 2:13:35

MySQL CDC 直接写进 Doris 汇总模型:同步即汇总,明细不用搬两遍

MySQL CDC 直接写进 Doris 汇总模型:同步即汇总,明细不用搬两遍 很多实时链路是这样做的:先把 MySQL 明细一条条同步到 Doris,再起一个定时任务做 GROUP BY,把结果写进汇总表。 链路长、延迟高、还多一份明细存储。 这…

作者头像 李华
网站建设 2026/10/31 2:13:34

门窗生产工序与出厂质检清单:八道工序、固化时间与核对项

一、八道工序与各自的控制点 工序一型材下料:按配料单把型材切成段,切口角度常按四十五度拼接控制;同批号与分支位置做标记。下料长度误差往往会放大成框扇对角线偏差,是链路里靠前的控制点。 工序二铣槽冲孔:铣出五金…

作者头像 李华
网站建设 2026/10/31 2:13:09

合并请求怎么合入:在保留提交、压缩和变基里选一种

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/31 2:12:25

Linux中部署Milvus保姆级教程

一、安装Docker 安装文档:如何安装Docker_cent 怎样安装 docker-CSDN博客 安装完成后,启动如下: 注意:如果是《使用 Docker Compose 运行 Milvus(Linux)》,那么需要检查是否存在docker compos…

作者头像 李华
网站建设 2026/10/31 2:12:15

ModBus

CRC16校验算法private void crc16(byte[] data){int[] temdata new int[data.Length 2];int xda, xdapoly;int i, j, xdabit;xda 0xFFFF;xdapoly 0xA001;for (i 0; i < data.Length - 2; i){xda ^ data[i];for (j 0; j < 8; j){xdabit (int)(xda & 0x01);xda …

作者头像 李华