news 2026/1/14 12:52:12

初识C语言(自定义结构:结构体)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
初识C语言(自定义结构:结构体)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、结构体类型的声明
    • 1. 结构体声明的基本语法
    • 2. 结构体成员的类型
    • 3. 结构体变量的定义
    • 4. 匿名结构体
    • 5. 结构体的初始化
    • 6. 结构体的内存布局
    • 7. 结构体的应用场景
  • 二、 结构体内存对⻬
    • 1. 内存对齐的基本概念
    • 2. 结构体对齐规则
    • 3. 对齐控制方式
    • 4. 实际案例分析
    • 5. 性能优化建议
    • 6. 现代C++的对齐支持
  • 三、结构体传参
  • 三、结构体传参
    • 1. 值传递
    • 2. 指针传递
    • 3. 选择建议
    • 4. 实际应用场景
    • 5. 注意事项
  • 四、 结构体实现位段
    • 1. 位段的基本语法
    • 2. 位段的特点和注意事项
    • 3. 位段的应用场景
    • 4. 示例代码
    • 5. 注意事项
  • 总结

前言

本文是关于结构体内容的介绍,在接下来的顺序表和链表实现中十分重要,应统小白又开始学习C语言啦!


一、结构体类型的声明

结构体是C语言中一种非常重要的复合数据类型,它允许将不同类型的数据组合成一个整体。结构体的声明格式如下:

struct结构体标签{数据类型 成员1;数据类型 成员2;...数据类型 成员n;};

1. 结构体声明的基本语法

结构体声明由以下几个部分组成:

  • struct关键字:表示开始一个结构体类型的声明
  • 结构体标签:用来标识这个结构体类型,遵循C语言标识符命名规则
  • 大括号{}:包含结构体的成员列表
  • 分号;:结束结构体声明

例如,声明一个表示学生的结构体:

structStudent{charname[20];// 姓名intage;// 年龄floatscore;// 成绩chargender;// 性别};

2. 结构体成员的类型

结构体成员可以是:

  • 基本数据类型(int, float, char等)
  • 数组类型
  • 指针类型
  • 其他结构体类型(嵌套结构体)
  • 联合体类型
  • 枚举类型

例如,包含多种类型成员的结构体:

structComplexStruct{intid;// 整型成员doublecoordinates[2];// 双精度浮点数组char*description;// 字符指针structDatebirth;// 嵌套结构体};

3. 结构体变量的定义

声明结构体类型后,可以定义该类型的变量:

// 方式1:先声明类型,再定义变量structStudent{charname[20];intage;};structStudentstu1,stu2;// 方式2:声明类型的同时定义变量structEmployee{charname[30];intempId;}emp1,emp2;// 方式3:使用typedef创建类型别名typedefstruct{floatx;floaty;}Point;Point p1,p2;

4. 匿名结构体

C语言允许声明没有标签的结构体,称为匿名结构体:

struct{intwidth;intheight;}rect1,rect2;

注意:匿名结构体只能在声明时定义变量,之后无法再创建该类型的变量。

5. 结构体的初始化

结构体变量可以在定义时初始化:

structStudent{charname[20];intage;floatscore;};// 完全初始化structStudentstu1={"张三",18,90.5};// 部分初始化(剩余成员自动初始化为0)structStudentstu2={"李四"};// 指定成员初始化(C99标准)structStudentstu3={.age=19,.score=85.5};

6. 结构体的内存布局

结构体在内存中的布局有以下特点:

  • 成员按照声明顺序依次存储
  • 可能存在内存对齐(padding)以优化访问效率
  • 总大小可能大于各成员大小之和

可以使用sizeof运算符获取结构体大小:

printf("Student size: %zu bytes\n",sizeof(structStudent));

7. 结构体的应用场景

结构体常用于表示现实世界中的复合实体,例如:

  • 图形程序中的点、矩形等几何对象
  • 学生信息管理系统中的学生记录
  • 游戏开发中的角色属性
  • 网络通信中的数据包格式
  • 文件系统中的目录条目

结构体为组织相关数据提供了便利,使得程序逻辑更加清晰,数据结构更加合理。

二、 结构体内存对⻬

1. 内存对齐的基本概念

内存对齐是指数据在内存中的存储位置必须满足特定地址要求的机制。具体来说,一个n字节的数据通常需要存储在n的整数倍地址上。

为什么要内存对齐?

  • 硬件效率:许多CPU架构对未对齐的内存访问会降低性能,甚至引发硬件异常
  • 缓存优化:对齐数据能更好地利用CPU缓存行(通常为64字节)
  • 原子操作:某些架构要求原子操作必须是对齐的

2. 结构体对齐规则

在C/C++中,结构体的对齐遵循以下规则:

  1. 成员对齐规则

    • 每个成员的偏移量必须是其自身大小或编译器指定对齐值(通过#pragma pack设置)的较小者的整数倍
    • 示例:int类型(通常4字节)的成员必须放在4的整数倍地址上
  2. 结构体整体对齐

    • 结构体总大小必须是其最大成员大小或编译器指定对齐值的整数倍
    • 这可能导致结构体尾部出现填充字节(padding)
  3. 嵌套结构体

    • 嵌套结构体按其最大成员对齐要求进行对齐

3. 对齐控制方式

开发者可以通过以下方式控制对齐:

  1. 编译器指令

    #pragmapack(n)// 设置对齐值为n字节#pragmapack()// 恢复默认对齐
  2. 属性声明(GCC/Clang):

    struct__attribute__((aligned(16)))MyStruct{inta;charb;};
  3. C11标准

    _Alignas(16)intx;// C11标准对齐方式

4. 实际案例分析

案例1:基础结构体

structExample1{chara;// 1字节// 3字节填充intb;// 4字节(必须从4的倍数地址开始)charc;// 1字节// 3字节填充(使总大小为最大成员int的整数倍)};// 总大小:12字节

案例2:调整对齐

#pragmapack(1)structExample2{chara;// 1字节intb;// 4字节(不再需要填充)charc;// 1字节};// 总大小:6字节#pragmapack()

5. 性能优化建议

  1. 成员排列策略

    • 按成员大小降序排列可以减少填充字节
    • 将相关数据尽量放在同一缓存行
  2. 特定场景优化

    • 网络传输:使用1字节对齐节省带宽
    • 数值计算:确保关键数据是缓存行对齐的
  3. 跨平台注意事项

    • 不同平台可能有不同的基本类型大小
    • 某些架构(如ARM)对未对齐访问有严格限制

6. 现代C++的对齐支持

C++11引入了对齐相关特性:

alignas(16)structAlignedStruct{...};// 指定对齐autoalign=alignof(AlignedStruct);// 获取对齐值

标准库还提供了std::aligned_storage等工具类来处理对齐存储问题。

三、结构体传参

三、结构体传参

在C语言中,结构体作为函数参数传递时有两种主要方式:值传递和指针传递。这两种方式各有特点和适用场景。

1. 值传递

值传递是指将整个结构体的副本作为参数传递给函数。这种方式会复制结构体的所有成员变量到函数内部。

特点:

  • 函数内部对结构体的修改不会影响原始结构体
  • 当结构体较大时,复制操作会带来性能开销
  • 语法简单直观

示例代码:

structStudent{charname[20];intage;floatscore;};voidprintStudent(structStudents){printf("Name: %s\n",s.name);printf("Age: %d\n",s.age);printf("Score: %.1f\n",s.score);}intmain(){structStudentstu={"Alice",20,89.5};printStudent(stu);// 值传递return0;}

2. 指针传递

指针传递是指将结构体的地址作为参数传递给函数,函数通过指针来访问和修改结构体成员。

特点:

  • 避免了结构体的复制,性能更高
  • 函数内部对结构体的修改会影响原始结构体
  • 需要使用指针运算符(->)访问成员

示例代码:

voidmodifyStudent(structStudent*s){strcpy(s->name,"Bob");s->age=22;s->score=92.5;}intmain(){structStudentstu={"Alice",20,89.5};modifyStudent(&stu);// 指针传递printStudent(stu);// 修改后的值会被打印return0;}

3. 选择建议

  • 当不需要修改结构体内容时,可以使用值传递
  • 当需要修改结构体或结构体较大时,建议使用指针传递
  • 对于只读访问,可以使用const指针传递,既保证效率又防止意外修改

4. 实际应用场景

  1. 学生管理系统:传递学生信息结构体进行成绩计算或信息修改
  2. 图形处理:传递包含坐标、颜色等属性的图形结构体
  3. 网络编程:传递包含IP地址、端口等信息的网络配置结构体

5. 注意事项

  • 避免传递过大的结构体值,可能造成栈溢出
  • 指针传递时要注意空指针检查
  • 考虑使用结构体指针作为返回值的情况

四、 结构体实现位段

在C语言中,结构体可以通过位段(bit-field)的方式来实现对内存空间的精细控制。位段允许我们将结构体成员定义为特定数量的位,而不是完整的字节,这在嵌入式系统开发或需要节省内存的场景中特别有用。

1. 位段的基本语法

位段的定义语法如下:

struct结构体名{类型说明符 成员名:位数;// 其他成员...};

例如,定义一个包含位段的结构体:

structStatus{unsignedintflag1:1;// 1位unsignedintflag2:3;// 3位unsignedintflag3:4;// 4位};

2. 位段的特点和注意事项

  1. 位段类型限制

    • 位段成员的类型只能是整型或枚举类型
    • 常用unsigned int/signed int或_Bool类型
    • C99标准后也可以使用intN_t等固定宽度类型
  2. 位段大小限制

    • 单个位段不能超过其基础类型的大小
    • 例如,unsigned int类型的位段在32位系统上最多32位
  3. 内存对齐

    • 位段成员在内存中的布局取决于编译器实现
    • 不同编译器可能对位段的存储方式有所不同
    • 可能因为对齐要求而产生填充位
  4. 特殊位段

    • 可以定义无名位段用于占位或填充
    structExample{unsignedinta:4;unsignedint:4;// 无名位段,4位填充unsignedintb:8;};
    • 可以定义0宽度位段强制下一个成员从新的存储单元开始
    structExample{unsignedinta:4;unsignedint:0;// 强制对齐unsignedintb:8;};

3. 位段的应用场景

  1. 硬件寄存器映射

    • 常用于描述硬件寄存器中各个位的功能
    • 例如描述状态寄存器或控制寄存器
  2. 协议数据包解析

    • 解析网络协议或通信协议中的标志位
    • 例如TCP头部中的各种控制标志
  3. 节省内存空间

    • 当需要存储大量布尔值或小范围数值时
    • 例如嵌入式系统中的状态标志集合
  4. 位操作替代

    • 比直接位操作更直观易读
    • 编译器会自动生成相应的位操作代码

4. 示例代码

下面是一个完整的位段使用示例:

#include<stdio.h>#include<stdint.h>// 定义包含位段的结构体structPacket{uint8_theader:4;// 包头,4位uint8_ttype:2;// 包类型,2位uint8_tpriority:1;// 优先级,1位uint8_treserved:1;// 保留位,1位uint8_tdata;// 数据部分,完整字节};intmain(){structPacketpkt;pkt.header=0xA;// 1010pkt.type=0x2;// 10pkt.priority=0x1;// 1pkt.reserved=0x0;// 0pkt.data=0x55;printf("Packet size: %zu bytes\n",sizeof(pkt));printf("Header: 0x%X\n",pkt.header);printf("Type: 0x%X\n",pkt.type);return0;}

5. 注意事项

  1. 可移植性问题

    • 位段的具体实现依赖于编译器
    • 不同平台可能有不同的字节序(大端/小端)
    • 跨平台代码需要特别注意
  2. 性能考虑

    • 位段的访问通常比普通变量慢
    • 频繁访问的位段可能会影响性能
  3. 地址操作限制

    • 不能对位段成员取地址
    • 因为位段可能不占用完整的可寻址存储单元
  4. 初始化限制

    • 位段成员不能用于数组初始化
    • 不能用于静态初始化器中的指定初始化

位段提供了一种高效利用内存的方式,但使用时需要权衡可移植性和性能等因素。在需要精确控制内存布局或与硬件交互的场景中,位段是一个非常实用的特性。

总结

以上就是本文总结的内容,熟练运用结构体对后来的研究非常重要。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/12 12:37:46

MediaPipe实时多模态感知:从单点检测到全身协同追踪的技术革命

MediaPipe实时多模态感知&#xff1a;从单点检测到全身协同追踪的技术革命 【免费下载链接】mediapipe Cross-platform, customizable ML solutions for live and streaming media. 项目地址: https://gitcode.com/GitHub_Trending/med/mediapipe 在移动计算和边缘AI快速…

作者头像 李华
网站建设 2026/1/3 12:04:57

SMDJ33A单向 TVS瞬态抑制二极管 :33V电压000W 浪涌,中压电路防护核心

SMDJ33A单向 TVS瞬态抑制二极管 二极管产品已经跟我们的生活有着密不可分的联系了&#xff0c; TVS瞬态抑制二极管&#xff0c;是一种高效能保护二极管&#xff0c;产品体积小、功率大、响应快等诸多优点&#xff0c;产品应用广泛MOS管 桥堆、肖特基整流二极管 TVS瞬态抑制二极…

作者头像 李华
网站建设 2026/1/13 18:37:24

MCP 2025量子编程认证重大升级(新增内容全曝光)

第一章&#xff1a;MCP 2025量子编程认证概述MCP 2025量子编程认证是微软推出的全新专业技术资格&#xff0c;面向下一代计算范式——量子计算领域。该认证旨在评估开发者在量子算法设计、量子电路构建以及使用Q#语言进行量子程序开发的综合能力。随着量子硬件逐步走向实用化&a…

作者头像 李华
网站建设 2026/1/12 19:16:46

Bottles:让Windows软件在Linux上轻松运行的智能解决方案

Bottles&#xff1a;让Windows软件在Linux上轻松运行的智能解决方案 【免费下载链接】Bottles Run Windows software and games on Linux 项目地址: https://gitcode.com/gh_mirrors/bo/Bottles 你是否曾经在Linux系统上尝试运行Windows软件时遇到各种兼容性问题&#x…

作者头像 李华
网站建设 2025/12/29 16:54:41

日志框架问答整理(吊打面试官)

基于AOP实现日志记录 HR 问答整理 一、核心亮点类问题 Q1&#xff1a;这套基于AOP的日志记录框架最核心的设计思路是什么&#xff1f; A1&#xff1a; 核心解决“日志记录侵入性高、数据采集碎片化、同步写入性能差”的核心问题&#xff0c;整体设计思路如下&#xff1a; 问题背…

作者头像 李华
网站建设 2026/1/12 23:33:44

从零到安全工程师:2025年必备技能树详解(附实战学习蓝图)

本文将全面解析网络安全学习路径&#xff0c;回答零基础学习者最关心的入门问题&#xff0c;分析2025年网络安全行业前景与就业状况&#xff0c;并深度剖析湖南省网安基地——这一国家与省级多重授牌的权威人才培养平台——的核心优势。通过对比其独有的政企背景、荣誉资质与本…

作者头像 李华