C语言结构体详解

招标信息爬取工具 一个能帮你节省大量时间的实用小工具,我自己用了挺久,分享出来给有需要的朋友 阅读详情

一、结构体类型的声明:打造自己的数据模板

结构体的本质是 "数据模板"—— 它定义了一组数据的组合方式,但本身不占用内存空间。声明一个结构体的基本语法如下:

struct 标签名 
{
    成员类型 成员名;
    //更多成员...
} 变量列表; //分号不能省略!

1.1 基本声明:描述具体对象

比如我们要描述一个学生,需要包含姓名、年龄、性别和学号,结构体声明如下:

struct Stu 
{
    char name[20]; //姓名
    int age;       //年龄
    char sex[5];   //性别
    char id[20];   //学号
}; //分号必须有!

这里的struct Stu就是一个结构体类型,我们可以用它来创建具体的变量。

1.2 特殊声明:匿名结构体

如果结构体只需要使用一次,可以省略 "标签名",称为匿名结构体:

struct 
{
    int a;
    char b;
    float c;
} x; //直接创建变量x,该结构体类型无法再次使用

注意:匿名结构体的类型是唯一的,即使成员完全相同,也会被编译器视为不同类型。例如下面的代码是非法的:

struct { int a; char b; } x;
struct { int a; char b; } *p;
p = &x; // 错误!编译器认为两者类型不同

1.3 结构体自引用:构建链表等递归结构

在实现链表、树等数据结构时,我们需要结构体中包含自身类型的成员,这称为自引用。但注意不能直接包含结构体本身(会导致无限嵌套,大小无法计算),而应该用指针:

//错误示例:包含自身变量,大小无限大
struct Node 
{
    int data;
    struct Node next; //错误!
};

//正确示例:包含自身指针
struct Node 
{
    int data;
    struct Node* next; //指针大小固定(4或8字节),合理
};

如果用typedef给结构体重命名,需避免匿名结构体提前使用新名称: 

//错误示例:匿名结构体中提前使用Node
typedef struct 
{
    int data;
    Node* next; //此时Node还未定义
} Node;

//正确示例:先声明带标签的结构体
typedef struct Node 
{
    int data;
    struct Node* next;
} Node; //正确,Node是struct Node的别名

 

二、结构体变量的创建和初始化

定义好结构体类型后,就可以创建变量并初始化了,主要有两种方式:

2.1 按成员顺序初始化

按结构体中成员的声明顺序依次赋值:

struct Stu 
{
    char name[20];
    int age;
    char sex[5];
    char id[20];
};

int main() 
{
    //按顺序初始化:姓名、年龄、性别、学号
    struct Stu s = {"张三", 20, "男", "20230818001"};
    //访问成员(用.操作符)
    printf("姓名:%s,年龄:%d\n", s.name, s.age);
    return 0;
}

2.2 指定成员初始化

可以通过成员名指定赋值,顺序不限:

int main() 
{
    //指定成员初始化,顺序可以打乱
    struct Stu s2 = {.age = 18, .name = "李四", .id = "20230818002", .sex = "女"};
    printf("姓名:%s,年龄:%d\n", s2.name, s2.age); //输出"李四,18"
    return 0;
}

 

三、重点:结构体内存对齐

结构体的大小计算是 C 语言的高频考点,也是难点。为什么struct {char a; int b;}的大小不是1+4=5字节?这就要理解内存对齐的规则。

3.1 对齐规则:决定成员的存储位置

内存对齐是编译器为了提高访问效率而采用的策略,具体规则如下:

  1. 第一个成员永远对齐到结构体起始位置(偏移量为 0)。
  2. 其他成员需对齐到 "对齐数" 的整数倍位置。
    对齐数 = min (编译器默认对齐数,成员自身大小)。
    • VS 默认对齐数为 8,Linux(gcc)默认无对齐数(对齐数 = 成员大小)。
  3. 结构体总大小必须是所有成员 "最大对齐数" 的整数倍。
  4. 嵌套结构体时,嵌套的结构体成员需对齐到自身最大对齐数的整数倍;整个结构体的总大小是所有成员(含嵌套结构体)最大对齐数的整数倍。

3.2 实例分析:用图理解内存布局

我们结合例子,用内存布局图直观展示(以 VS 环境为例,默认对齐数 8)。

例 1:struct S1
struct S1 
{
    char c1;  //大小1
    int i;    //大小4
    char c2;  //大小1
};

  • 步骤 1:c1是第一个成员,放在偏移量 0 处,占 1 字节(0~0)。
  • 步骤 2:i的对齐数 = min (8, 4) = 4,需放在 4 的整数倍位置(偏移量 4),占 4 字节(4~7)。
  • 步骤 3:c2的对齐数 = min (8, 1) = 1,放在 8(7+1)处,占 1 字节(8~8)。
  • 总大小:最大对齐数是 4,需满足 4 的整数倍。当前已用 9 字节,向上取整到 12 字节。
偏移量:0 1 2 3 4 5 6 7 8 9 10 11
成员:  c1 空 空 空 i  i  i  i c2 空 空 空

 sizeof(struct S1) = 12。

 例 2:struct S2(成员顺序优化)

struct S2 
{
    char c1;  //1字节
    char c2;  //1字节
    int i;    //4字节
};
  • 步骤 1:c1在 0(0~0),c2对齐数 1,接在 1(1~1)。
  • 步骤 2:i对齐数 4,放在 4(4~7)。
  • 总大小:最大对齐数 4,总大小 8(0~7),刚好是 4 的倍数。
偏移量:0 1 2 3 4 5 6 7
成员:  c1 c2 空 空 i  i  i  i

sizeof(struct S2) = 8(比 S1 节省 4 字节,可见成员顺序的重要性)。 

例 3:嵌套结构体 struct S4 

struct S3 
{
    double d; //8字节
    char c;   //1字节
    int i;    //4字节
};
struct S4 
{
    char c1;  //1字节
    struct S3 s3; //嵌套S3
    double d; //8字节
};

先算struct S3的大小:

  • d在 0(0~7),对齐数 8。
  • c对齐数 1,放在 8(8~8)。
  • i对齐数 4,需放在 12(8+1=9,最近的 4 的倍数是 12),占 12~15。
  • 最大对齐数 8,总大小需是 8 的倍数(当前 16 字节,满足)。所以sizeof(S3)=16。

再算struct S4:

  • c1在 0(0~0)。
  • s3是嵌套结构体,最大对齐数 8,需放在 8 的倍数(偏移 8),占 8~23(16 字节)。
  • d对齐数 8,放在 24(23+1=24,8 的倍数),占 24~31(8 字节)。
  • 最大对齐数 8,总大小 32(31-0+1=32,是 8 的倍数)。
偏移量:0 1-7 8-23(s3) 24-31(d)
成员:  c1  空   S3的16字节   d的8字节

sizeof(S4)=32。 

3.3 为什么需要内存对齐?

  1. 平台兼容性:某些硬件只能访问特定地址的数据(如只能从 4 的倍数地址读 int),否则抛出异常。
  2. 性能优化:对齐的内存只需 1 次访问,未对齐可能需要 2 次。例如:若 int 放在偏移 1 处,处理器需先读 0~3 字节,再读 4~7 字节,拼接后才能得到完整 int。

本质是用空间换时间的策略。

3.4 如何优化:平衡对齐和空间

要减少内存浪费,应将小成员集中在一起(如 S2 比 S1 节省空间)。例如:

//差:小成员分散
struct Bad 
{
    char a;    //1
    double b;  //8
    char c;    //1
}; //大小:24(0+8+8+8,最大对齐数8)

//好:小成员集中
struct Good 
{
    char a;    //1
    char c;    //1
    double b;  //8
}; //大小:16(0+1+6空+8,总16)

3.5 修改默认对齐数

用#pragma pack(n)可以修改默认对齐数(n 为 1、2、4、8 等),#pragma pack()恢复默认。

#pragma pack(1) //设置默认对齐数1(无对齐)
struct S 
{
    char c1; //1
    int i;   //4
    char c2; //1
};
#pragma pack() //恢复默认

 此时对齐数 = 成员大小,总大小 = 1+4+1=6 字节。

四、结构体传参:传地址更优

结构体传参时有两种方式:传值和传地址。推荐传地址:

struct S 
{ 
    int data[1000]; 
    int num; 
};

//传值:复制整个结构体,开销大
void print1(struct S s) 
{ 
    printf("%d", s.num); 
}

//传地址:只复制指针(4/8字节),高效
void print2(struct S* ps) 
{ 
    printf("%d", ps->num); 
}

原因:函数传参时参数需压栈,结构体过大时压栈开销大,导致性能下降。

总结

结构体是 C 语言中自定义复杂类型的核心工具,掌握以下几点:

  • 声明时注意匿名结构体的限制和自引用的正确方式(用指针)。
  • 初始化支持按顺序和指定成员两种方式。
  • 内存对齐是重点:理解 4 条规则,通过实例画图分析大小,记住 "空间换时间" 的本质,合理安排成员顺序优化空间。
  • 传参优先传地址,减少开销。

通过练习结构体大小计算,能更深入理解内存对齐的细节,应对考试和实际开发中的问题。

 

 

 

基于Cesium移动的天空云 天空中的云朵也算是一种天气气候的效果,它能更加逼真的反映当前的天气状况,可以是白云朵朵,也可以是乌云密布,结合其他天气效果,可以组合形成较为真实的三维可视化场景。 阅读详情

相关推荐

从零开始用STM32驱动RGB屏:配置PCLK、DE同步信号的避坑指南

本文详细介绍了如何使用STM32驱动RGB接口LCD屏幕,重点解析了PCLK配置、同步信号极性和DE使能等关键参数的设置方法。通过实战代码示例和常见问题排查指南,帮助开发者避免常见配置错误,实现稳定的显示效果。特别针对800x480 IPS屏提供了优化建议和调试技巧。

sky77的博客 540

C语言 | 结构体使用详解

本文更好的排版方式:【C语言笔记】结构体 我们都知道C语言中变量的类型决定了变量存储占用的空间。当我们要使用一个变量保存年龄时可以将其声明为int类型,当我们要使用一个变量保存某一科目的考试成绩时可以将其声明为float。 那么,当我们要做一个学生信息管理系统时,需要保存学生的姓名、学号、年龄等信息,该怎么做呢? 如当要保存三个学生的信息时, 方法一是: char *name1,*name2,...

嵌入式大杂烩 3万+

同步辐射材料科学应用指南:从XAFS原位实验到数据处理

材料结构分析是材料研究的核心环节,但传统实验室X射线受限于能量固定、亮度不足,往往难以回答原子局域配位、非晶结构及动态演变等关键问题。同步辐射光源以高通量、连续可调、偏振可控及脉冲时间结构等特性,将材料表征从静态观察推进到原位动态追踪层次。基于X射线吸收精细结构谱(XAFS)、原位实验、X射线衍射与散射、微区成像等技术,研究人员可实现元素选择性分析、近邻原子环境解析及真实工况下的结构演变监测。本文从技术原理出发,覆盖实验方案设计、样品制备、现场对光、数据处理与常见故障排查,并结合典型应用场景,为材料科学领域

weixin_32612263的博客 165

c语言结构体详解

char c;int i;double d;讲解按顺序,先是s2.c1 ,该为字符,大小为1个字节,<8,所以对齐数为1又因为起始位置偏移量为0所以接着就是嵌套结构体了,开始进入结构体s3s3.d为double类型,大小为8个字节 == 8,所以对齐数为8又因为起始位置要为对齐数的整数倍所以最终为。

L070117的博客 2394

使用python排八字 计算八字的相合相冲五行分值等 ...

简介 八字术是一种古老的算命术。它运用阴阳、五行、干支、八卦等理论,把人的出生年月日时转化为八个字,进而推算人的富贵贫贱、祸福吉凶、穷通寿天、名利地位、家庭六亲等方面的情况 八字源自古代的中国,是一种利用天干和地支来准确记录年、月、日、时的方式,由“年干,年支”、“月干,月支”、“日干,日支”、“时干,时支”,共八个干支所组成(共八个字),年、月、日、时...

weixin_33717298的博客 5310

LLM and Embedding区别

LLM(大语言模型)和 Embedding(嵌入)是自然语言处理(NLP)中的两个核心概念,它们的核心区别在于。Embedding 计算向量。

u010674101的专栏 1874

C语言详解:结构体

结构体 当然本次结构体仍不会讲的特别深入,在后面的自定义类型进阶会更加深入的讲到这部分内容。 结构(体)是一些值的集合,这些值被称为成员变量。结构的每个成员变量可以具有不同类型。 我们所学过的类型如:char,int,float,double等,都只能描述单一变量。但是结构体,顾名思义,是多个变量的集合,其中包含多个单一变量。所以C语言就发明了结构体用于用来描述复杂对象,如:书,人等具有多个特征的变量。 结构体类型的声明 结构体声明的语法结构 struct tag//结构体名称 { member

yourfriendyo的博客 2万+

Oracle EBS 如何处理MMT表错误-WIP 实体未定义或不具有期间余额项

错误说明:WIP 实体未定义或不具有期间余额项。(The wip entity is either not defined or does not have a period balance entry)由于工单的创建日期为4月份,工单的余额表记录的期间是4月份,而3月份工单余额表中没有记录。公司将上一个新的库存组织,有一部分测试的工单创建日期在4月份,而工单的发放日期、发料事务处理日期和完工事务处理日期均在3月份。1、关闭3月份库存会计期,待定事务处理存在未计成本物料/WMS。成本类型: PAC成本。

陈显平的专栏-Oracle EBS 652

C语言 结构体详解

C语言为我们提供了基本的数据类型,例如int、char、float等,但我们在实际生活中的对象都是复杂的,不能仅靠一种数据简单的描述。我们回顾一下数组,数组是一种自定义类型,比如,它的类型就为int [10],自定义类型使我们能够更加灵活的解决问题。而结构体同样是一种自定义类型。而结构体就实现了对一个对象进行多方面描述的功能。

qq_67140973的博客 1205

无需SLAM建图,直接生成Gazebo仿真环境的二维代价地图的功能包pgm_map_creator适配ROS Noetic编译报错及解决方法

在ROS Noetic中,使用`pgm_map_creator`功能包直接生成Gazebo仿真环境的二维代价地图时,可能会遇到各种编译和运行错误。本篇博客详细解析了在适配ROS Noetic过程中常见的报错及其解决方法,包括Boost文件结构更新、C++版本兼容性、Gazebo函数名变更、链接库缺失以及protobuf文件加载错误。通过逐步排查和修改,如更换头文件路径、调整C++标准、更新CMakeLists.txt文件等,成功解决了这些问题,使得`pgm_map_creator`功能包能够顺利编译和运行。

慕羽★的博客 2261

室内无线传播损耗分析与传播模型验证课程设计

室内无线传播损耗分析与传播模型验证课程设计

a_zxswer的博客 187

【C语言】结构体详解

在计算机编程的世界中,数据的组织和存储是至关重要的。C语言作为一门强大而灵活的编程语言,提供了结构体(Structures)这一强大的工具,使得我们能够更加有效地组织和管理数据。结构体是C语言中一种用户自定义的数据类型,它允许我们将不同数据类型的元素组合在一起,形成一个更为复杂的数据结构。通过结构体,我们可以模拟现实世界中的实体,将相关的信息组织起来,使得代码更加清晰、可读性更强。在这篇博客中,我们将深入探讨C语言结构体的概念、用法以及它在程序设计中的实际应用。

m0_68002296的博客 2111

在 SAP 中,应收票据的收取、到期承兑、贴现均有标准操作路径与事务码,以下按业务场景给出操作步骤、T-code、会计分录(含附 / 不附追索权)

摘要:本文详细介绍了SAP系统中应收票据的全流程处理,包括收款(F-36)、到期承兑(F-34/F-20)和贴现(F-33)的标准操作。重点说明了不同业务场景下的事务码使用、操作步骤及对应的会计分录,特别区分了附追索权与不附追索权贴现的处理差异。同时提供了票据查询(FBL5N)和清账(F-32)的辅助功能指引,并汇总了各业务场景的核心会计分录,为财务人员提供了一套完整的SAP应收票据操作指南。(149字)

金牌架构师 263

C语言详解(结构体)

结构体详解

小羊的博客 2798

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

【c语言】详解结构体

【c语言】详解结构体

一起学习,进大厂!!! 1322

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

C语言结构体typedef struct详解

1、typedef关键字 C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。下面的实例为单字节数字定义了一个术语 BYTE: typedef unsigned char BYTE; 在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如: BYTE b1, b2; typedef相当于给unsigned char起...

羊脂球的博客 3万+

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

C语言中的结构体(struct)详解

C语言中,结构体类型属于一种构造类型(其他的构造类型还有:数组类型,联合类型)。本文主要介绍关于结构体以下几部分。 1、概念 为什么要有结构体? 因为在实际问题中,一组数据往往有很多种不同的数据类型。例如,登记学生的信息,可能需要用到 char型的姓名,int型或 char型的学号,int型的年龄,char型的性别,float型的成绩。又例如,对于记录一本书,需要 char型的书名,char型的作者名,float型...

edward_zcl的博客 6万+

C语言-结构体-详解

C语言中,结构体的详解。

夜泉的博客 2736

C语言结构体struct详解

在实际的开发过程中,一组数据往往具有不同的数据类型,此时数组是不能够满足需求了。因为数组中各元素的类型必须是一致的。为了解决这个需求,C中给出了另一种数据类型-结构体,每一个成员可以是任意一种数据类型...

Somnus陳的博客 4001

C语言结构体(详解)

int age;int age;int age;} Student;

m0_61315608的博客 5952

c语言-结构体详解

结构体(struct)是C、C++、C#等编程语言中的一种复合数据类型,它允许您把多个不同类型的数据项组合成一个单独的类型。通过使用结构体,您可以创建一个自定义的数据类型,该类型包含多个字段,每个字段可以存储不同类型的数据。在C语言中,结构体的定义通常如下:struct 结构体名 { 数据类型 成员变量名1; 数据类型 成员变量名2; ... 数据类型 成员变量名N; };使用场景:通常定义一个变量时,比如人,拥有多个特性,名字,年龄, 性别等.这时我们可

2301_81336748的博客 1919
上一篇: 数据在内存中的存储
下一篇: C语言实现贪吃蛇游戏
THGML
博客等级 码龄3年 49粉丝 21原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值