[C]基础17.自定义类型:结构体

article/2025/8/12 6:45:20
  • 博客主页:向不悔
  • 本篇专栏:[C]
  • 您的支持,是我的创作动力。

文章目录

  • 0、总结
  • 1、结构体类型的声明
    • 1.1 结构的声明
    • 1.2 结构体变量的创建和初始化
    • 1.3 结构体的特殊声明
    • 1.4 结构的自引用
    • 1.5 typedef与结构自引用
  • 2、结构体内存对齐
    • 2.1 对齐规则
    • 2.2 为什么存在内存对齐?
    • 2.3 修改默认对齐数
  • 3、结构体传参
  • 4、结构体实现位段
    • 4.1 什么是位段
    • 4.2 位段的内存分配
    • 4.3 位段的跨平台问题
    • 4.4 位段的应用场景
    • 4.5 位段使用的注意事项


0、总结

在这里插入图片描述

1、结构体类型的声明

结构体是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

1.1 结构的声明

struct tag {member-list;
} variable-list;

例如描述一个学生

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

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

#include <stdio.h>
struct Stu
{char name[20];    //名字int age;          //年龄char sex[5];      //性别char id[20];      //学号
};int main()
{//按照结构体成员的顺序初始化struct Stu s = { "张三", 20, "男", "20250529001" };printf("name :%s\n", s.name);printf("age  :%d\n", s.age);printf("sex  :%s\n", s.sex);printf("id   :%s\n", s.id);printf("-----------------\n");//按照指定的顺序初始化struct Stu s2 = { .age = 18,.name = "lisi",.id = "20250530002",.sex = "女" };printf("name :%s\n", s2.name);printf("age  :%d\n", s2.age);printf("sex  :%s\n", s2.sex);printf("id   :%s\n", s2.id);return 0;
}
运行:
name :张三
age  :20
sex  :男
id   :20250529001
-----------------
name :lisi
age  :18
sex  :女
id   :20250530002

1.3 结构体的特殊声明

在声明结构的时候,可以不完全的声明(匿名结构体)。

// 匿名结构体类型
struct {int a;char b;float c;
} x;struct {int a;char b;float c;
} a[20], *p;

注意:编译器会把上面的两个声明当成完全不同的两个类型,所以下面的代码是非法的:

p = &x;  // 非法操作

思考:为什么非法?

  • 类型不同:即使两个匿名结构体成员完全相同(int a; char b; float c;),编译器仍将它们视为完全不同的类型。
  • 匿名特性:没有结构体标签(tag),无法声明相同类型。
  • 指针不兼容:p是指向类型B的指针,&x是类型A的地址,类型不匹配。

如果要解决,可以这样:

  • 1、添加标签

    struct Standard {  // 添加标签int a; char b; float c;
    };
    struct Standard x;
    struct Standard a[20], *p;
    p = &x; // 合法
    
  • 2、使用 typedef 统一类型

    typedef struct {  // 创建类型别名int a; char b; float c;
    } MyType;
    MyType x;
    MyType a[20], *p;
    p = &x; // 合法
    

警告:匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。

1.4 结构的自引用

结构自引用指在结构体内部包含指向自身类型结构体的指针成员,这是实现链式数据结构(如链表、树等)的基础。

在结构中包含一个类型为该结构本身的成员:

struct Node {int data;struct Node next;  // 错误,会导致无限大小
};

这种写法是错误的,因为一个结构体中再包含一个同类型的结构体变量,这样结构体变量的大小就会无穷大。

Node = int + Node= int + (int + Node)= int + (int + (int + Node))... // 无限循环

正确的自引用方式

struct Node {int data;struct Node* next;  // 使用指针
};

优势

  • 固定大小:指针大小固定(4/8字节),不引起递归问题

  • 内存高效:仅存储地址,不复制整个结构

1.5 typedef与结构自引用

错误用法分析:

typedef struct {     // 匿名结构体int data;Node* next;      // 错误:Node尚未定义
} Node;

问题原因:

  • 编译顺序:编译器先处理结构体定义

  • 未定义类型:在结构体内部使用Node*时,Node尚未被定义

解决方案:定义结构体不要使用匿名结构体。

  • 完整标签声明

    typedef struct Node {  // 明确结构体标签int data;struct Node* next; // 使用标签声明指针
    } Node;                // typedef别名
    
  • 分步声明

    struct Node;           // 前向声明typedef struct Node {int data;struct Node* next; // 使用前向声明
    } Node;
    

总结:

  • 结构自引用必须使用指针而非完整结构体

  • typedef自引用需配合结构体标签或前向声明

  • 匿名结构体不适合需要自引用的场景

  • 正确使用自引用可实现高效链式数据结构

2、结构体内存对齐

2.1 对齐规则

首成员规则:结构体的第一个成员对齐到偏移量为0的地址处

成员对齐规则:其他成员变量要对齐到"对齐数"的整数倍地址

  • 对齐数 = min(编译器默认对齐数, 成员自身大小)
  • VS编译器默认对齐数=8,Linux gcc无默认对齐数(对齐数=成员大小)

结构体大小规则:结构体总大小为最大对齐数(所有成员中对齐数的最大值)的整数倍

嵌套结构体规则

  • 嵌套的结构体成员对齐到其内部成员的最大对齐数的整数倍处
  • 整体结构体大小是所有最大对齐数(含嵌套结构体成员)的整数倍

练习1:struct S1

struct S1 {char c1;  // 大小1,对齐数min(8,1)=1int i;    // 大小4,对齐数min(8,4)=4 → 需对齐到4的倍数char c2;  // 大小1,对齐数1
};
// 总大小:12字节(最大对齐数4的倍数)
0    1   2   3   4   5   6   7   8   9   10  11
[c1] 空  空  空  [i] [i] [i] [i] [c2] 空  空  空

练习2:struct S2

struct S2 {char c1;  // 大小1,对齐数1char c2;  // 大小1,对齐数1int i;    // 大小4,对齐数4
};
// 总大小:8字节(最大对齐数4的倍数)
0   1   2   3   4   5   6   7
[c1][c2] 空  空  [i] [i] [i] [i]

练习3:struct S3

struct S3 {double d;  // 大小8,对齐数min(8,8)=8char c;    // 大小1,对齐数1int i;     // 大小4,对齐数4 → 需对齐到4的倍数
};
// 总大小:16字节(最大对齐数8的倍数)
0-7                         8   9   10  11   12-15
[d][d][d][d][d][d][d][d]   [c]  空  空   空   [i][i][i][i]

练习4:嵌套结构体

struct S4 {char c1;       // 大小1,对齐数1struct S3 s3;  // 大小16,对齐数8(S3中最大对齐数)double d;      // 大小8,对齐数8
};
// 总大小:32字节(最大对齐数8的倍数)
0    1-7          8-23           24-31
[c1] 空(对齐)    [s3:16字节]     [d][d][d][d][d][d][d][d]

练习5:混合字符串类型

struct MixedString {char *pname;    // 指针8字节,对齐数8char name[12];  // 字符数组,对齐数1int age;        // 4字节,对齐数4
};
// 总大小:32字节
0-7  : [pname]    // 指针(8)
8-19 : [name]    // char[12] (12字节)
20-23: [age]    // int(4)
24-31: 填充      // 总大小32(最大对齐数8的倍数)

练习6:数组+嵌套结构体

struct Inner {char c;     // 1字节,对齐数1double d;   // 8字节,对齐数8 → 最大对齐数8
};
// 总大小:16字节struct Outer {int arr[2];         // 元素大小4,对齐数4struct Inner in;    // 嵌套结构体,对齐数8short s;            // 2字节,对齐数2
};
// 总大小:32字节
0-7  : [arr]      // int[2] (4×2=8字节)
8-15 : [in.c]    // Inner.c
16-23: [in.d]   // Inner.d
24-25: [s]      // short(2)
26-31: 填充      // 总大小32(最大对齐数8的倍数)

2.2 为什么存在内存对齐?

两大核心原因:

平台兼容性

  • 不是所有硬件都能访问任意地址上的任意数据
  • 某些硬件平台只能在特定地址访问特定类型数据
  • 未对齐访问可能导致硬件异常

性能优化

  • 对齐的内存访问只需一次操作

  • 未对齐访问可能需要两次操作

  • 示例:读取8字节double类型

    对齐地址(8的倍数):一次读取完成
    未对齐地址:可能跨越两个内存块,需要两次读取+数据拼接
    

空间优化技巧

  • 让占用空间小的成员尽量集中在一起

  • 对比S1和S2:成员相同但大小不同,S2更优!

    struct S1 { char c1; int i; char c2; }; // 12字节
    struct S2 { char c1; char c2; int i; };  // 8字节
    

2.3 修改默认对齐数

使用 #pragma pack 指令

#include <stdio.h>#pragma pack(1) // 设置默认对齐数为1
struct S {char c1;  // 大小1,对齐数min(1,1)=1int i;    // 大小4,对齐数min(1,4)=1char c2;  // 大小1,对齐数1
};
#pragma pack() // 恢复默认对齐数int main() {printf("%d\n", sizeof(struct S)); // 输出:6return 0;
}

使用场景

  • 空间敏感场景:网络传输、嵌入式系统

  • 兼容特定硬件:需要1字节对齐的设备

  • 数据打包:文件存储格式定义

注意:修改对齐数可能导致性能下降,仅在必要时使用

3、结构体传参

#include <stdio.h>struct S {int data[1000]; // 4000字节数组int num;        // 4字节
};void print1(struct S s) {      // 传结构体值printf("%d\n", s.num);
}void print2(struct S* ps) {    // 传结构体地址printf("%d\n", ps->num);
}int main() {struct S s = { {1,2,3,4}, 1000 };print1(s);   // 传结构体print2(&s);  // 传地址return 0;
}
运行:
1000
1000

上面的print1print2函数哪个好些?

答案是:首选print2函数

原因:

  • 函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
  • 如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。

总结:结构体传参的时候,要传结构体的地址。

4、结构体实现位段

4.1 什么是位段

位段(Bit-field)是C语言中一种特殊的数据结构,它允许我们将多个变量紧凑地存储在同一个字节或字中,从而有效地节省内存空间。

位段的声明方式与结构体类似,但有两点关键区别:

  • 位段成员必须是整型(如intunsigned intsigned int),在C99标准中也可以使用其他类型

  • 每个成员名后需要跟一个冒号和数字,表示该成员占用的位数

    struct A {int _a:2;    // 占用2位int _b:5;    // 占用5位int _c:10;   // 占用10位int _d:30;   // 占用30位
    };
    

4.2 位段的内存分配

位段在内存中的分配方式有以下特点:

  • 空间按需分配,通常以4字节(int)或1字节(char)为单位开辟
  • 位段成员在内存中的排列顺序(从左到右或从右到左)取决于编译器和平台
  • 当一个位段无法容纳在剩余空间时,可能舍弃剩余位或利用下一单元,这由编译器决定
struct S {char a:3;char b:4;char c:5;char d:4;
};

在VS2013环境下,上述结构体可能的内存布局如下(假设从右向左分配):

  • 第一个字节:a(3位) + b(4位) + 1位剩余
  • 第二个字节:c(5位) + 3位剩余
  • 第三个字节:d(4位)

4.3 位段的跨平台问题

位段虽然节省空间,但存在严重的跨平台问题:

  • int位段可能被视为有符号数或无符号数,标准未定义

  • 位段最大位数不确定(16位机器最大16,32位机器最大32)

  • 成员在内存中的分配方向(从左到右或从右到左)未标准化

  • 当空间不足时,是舍弃剩余位还是利用下一单元,标准未定义

总结: 跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

4.4 位段的应用场景

位段特别适合用于需要精确控制数据位数的场景,如:

  • 网络协议:IP数据报头部包含许多只需要几位表示的字段

  • 硬件寄存器:与硬件寄存器位对应

  • 嵌入式系统:内存受限的环境下节省空间

4.5 位段使用的注意事项

  • 不能取地址:位段成员可能不位于字节起始位置(几个成员共有同一个字节),因此没有地址

  • 输入处理:不能直接用scanf读取位段成员,需要通过临时变量中转

  • 可移植性:注重可移植性的代码应避免使用位段

// 错误示例
scanf("%d", &sa._b);  // 错误,不能取位段地址// 正确做法
int b = 0;
scanf("%d", &b);
sa._b = b;

总结:

位段是一种强大的内存优化工具,能够将多个小范围值紧凑地存储在内存中,特别适合内存受限的环境或需要精确控制数据位数的场景。

然而,其跨平台问题使得它在需要可移植性的应用中受到限制。在使用位段时,开发者需要在节省空间和保证可移植性之间做出权衡。


http://www.hkcw.cn/article/FqJZGrCsLH.shtml

相关文章

俄为何严厉警告德国 地缘政治博弈升级

近日,一场外交风波引发国际社会高度关注。俄罗斯政府对德国新总理默茨发出严厉警告,称德国已经直接卷入了对俄战争。这一警告背后,蕴含着深刻的历史隐喻和当前地缘政治的复杂博弈。5月28日,俄罗斯外长拉夫罗夫在接受采访时明确表示,德国已经直接卷入乌克兰战争,正沿着导致…

印媒涉歼10C言论有何意味 歼-10C颠覆战场格局

180公里外一道火光划破天际,印度飞行员甚至未能捕捉到敌机的影子,其引以为傲的“阵风”战机便已在长空折翼。这并非虚构场景,而是印巴冲突中歼-10C战机横空出世的真实战果。印度媒体曾狂妄宣称:若无歼-10C,印军凭借压倒性优势,完全可以“压着巴铁打”,甚至给巴基斯坦带去…

如何看待俄媒强势回应德援乌举措 冲突升级风险增加

5月27日,俄罗斯总统新闻秘书佩斯科夫表示,如果欧洲国家取消对乌克兰远程攻击武器的射程限制,这将意味着冲突升级。此前,德国总理默茨宣布,德国及其盟友不再限制援助乌克兰武器的射程,允许乌克兰使用西方武器打击俄境内军事目标。默茨指出,俄罗斯最近对乌克兰城市和非军事…

成都辟谣公路边停车位免费 网传信息不实

近日,有网民在网络平台发布消息称,从2025年6月1日起,成都市所有公路边停车位将不再收取停车费,市民可以免费使用。这一消息引起了广泛关注。5月29日,成都交投智慧停车产业发展有限公司通过微信公众号“成都停车APP”发布声明,称网传信息不实。公司表示未收到上级单位停止…

王石是否会回归万科?万科走到今天这一步,王石难道没有责任吗?

万科走到今天这一步,王石难道没有责任吗?今天,一张疑似来自王石朋友圈的配图到处流传,其中写道:“在外界看来,王石就是万科,万科就是王石。我认可:万科是我创建的、制度是我建立的、团队是我培养的、接班人是我选择的。岂能推卸对万科因(阿狸注:原文如此,应为”应“…

北京举办“交通开放日”活动 绿色出行理念深入人心

为深入推进交通文化普及,培养青少年绿色出行意识,5月30日“六一”儿童节前夕,市交通委与北京第二实验小学德胜校区一年级同学在北京公交馆联合举办了一场别开生面的专场“交通开放日”活动。80名小学生及其家长积极参与,让交通文化的种子在孩子们心中生根发芽,并通过“小手…

专家解读我军航母战斗群与轰-6动向 南海部署引发热议

卫星图像显示,本月中国在南海西沙群岛部署了两架最先进的轰-6K战略轰炸机。这是自2020年以来,这款远程轰炸机首次出现在该地区,引发了广泛关注。此外,卫星图像还捕捉到了两架运-20运输机和一架空警-500预警机的身影,这些新到来的战机与原本驻扎在机场的战机共同构成了一个…

小学生半个月遭母亲殴打十余次 强制报告制度启动援助程序

近日,司法部发布了一起未成年人法律援助的典型案例。案例中提到,江苏省某小学老师发现学生胡某某身上有多处新旧伤痕,询问后得知胡某某因不愿意上学等问题被母亲多次殴打。老师随即按照强制报告制度要求向检察机关报告。经鉴定,胡某某挫伤面积达体表面积8%,构成人体轻伤一…

成都警方通报男子持刀划伤2人后自残 最新警情公布

新京报讯 5月30日,成都市公安局锦江区分局发布警情通报:编辑 毛天宇责任编辑:0764

北京航空航天大学计算机保研上机真题

2025北京航空航天大学计算机保研上机真题 2024北京航空航天大学计算机保研上机真题 2023北京航空航天大学计算机保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/problem?classification1 个位为1的素数 题目描述 输入一个整数 n n n( 2 ≤ n ≤ 10000 2 \leq n \…

断眉《歌手2025》袭榜成功 音乐鬼才亮相

5月30日,湖南卫视《歌手》官方微博宣布查理普斯为袭榜歌手,他的音乐合伙人是蒋一侨。官博称赞他为“音乐创作鬼才”,不仅才华横溢,现场演唱也感染力十足。他将带来两首歌曲《Attention》和《See You Again》。不过,湖南卫视未透露他的袭榜对象。2024年5月31日晚,在《歌手…

张雨绮韩庚新剧结成“养娃搭子” 共赴育儿奇妙之旅

电视剧《绽放的许开心》将于6月5日在江苏卫视幸福剧场和腾讯视频同步播出,由张雨绮和韩庚领衔主演。剧中,许开心的生活充满了挑战:前夫和闺蜜的双重背叛让她带着两个孩子净身出户。尽管如此,她依然乐观坚强,白天是职场达人,晚上则是陪读哄睡的超人妈妈,在困境中活出了当…

全新小鹏P7动态影像曝光 实车尾翼亮相

博主Rich叮近日发布了一组全新小鹏P7的实车动态影像。视频中的两辆新车分别采用了亮银色和哑光紫色的配色,但这些颜色可能是车衣的效果。新车张开了尾翼,外观棱角分明,后窗区域与车身同色,呈现出“无后窗”的视觉效果。这款新车由法国设计师Rafik Ferrag设计,采用了最新的…

《绽放的许开心》6月5日开播 演绎2+24治愈生活

电视剧《绽放的许开心》将于6月5日在江苏卫视每日鲜语幸福剧场和腾讯视频同步播出,由张雨绮、韩庚领衔主演。这部剧讲述了一个单亲妈妈与“养娃搭子”之间发生的温馨故事。许开心是一位坚强的单亲妈妈,她经历了前夫和闺蜜的双重背叛,带着两个孩子独自生活。白天她在职场上雷…

【速通RAG实战:进阶】13、企业知识引擎构建实战:数据处理、RAG系统与向量数据库应用

一、企业知识组织的战略价值与技术框架 (一)知识数字化转型的核心目标 在数字化时代,企业知识已成为核心竞争力的关键要素。据Gartner报告显示,高效的知识管理可使员工决策效率提升30%,创新成本降低40%。企业知识组织的核心目标可归纳为“四化”: 整合化:打破部门壁垒…

U16国少连丢两球2-2越南U16 防守问题再现

5月30日晚,U16国足在呼和浩特国际足球锦标赛第二轮比赛中对阵越南队。上半场U16国足表现出色,以2比0领先。然而下半场被对手连追两球,最终双方2比2战平。尽管最后一个丢球发生在球队少一人作战的情况下,但连续两场比赛在下半场丢球,显示出这支球队在防守和注意力方面存在问…

海边游玩需警惕哪些安全隐患 涨潮与离岸流风险

夏季去海边游玩是很多人避暑纳凉的选择。然而,海滩在强降雨或涨潮时暗藏风险。近期,各地消防部门接到了多起游客在海边遇险的警情。近日,山东日照消防接到求助,5名游客在某海滩公园赶海过程中因涨潮被困在礁石上。消防员驾驶橡皮艇赶到现场,发现海水已没过礁石和游客膝盖。…

修建豪华公厕 原厅长李德明被通报 擅自变更方案造价翻倍

5月30日,吉林省纪委监委公开通报了四起形式主义、官僚主义典型问题,其中包括今年1月主动投案的吉林省农业农村厅原厅长李德明。据通报,李德明在担任通榆县委书记期间,擅自变更城区新建公共卫生间项目的设计方案,扩大修建面积并进行豪华装修,导致造价相较预算翻倍。吉林市…

历年贵州大学计算机保研上机真题

2025贵州大学计算机保研上机真题 2024贵州大学计算机保研上机真题 2023贵州大学计算机保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/problem?classification1 字符串翻转 题目描述 给定一个字符串&#xff0c;反序输出。 输入格式 输入一个字符串在单独的一行…

王楚钦说奥运会后的三个月很难熬 内心挣扎与成长

5月30日,央视发布了《面对面王楚钦蜕变之路》节目预告。预告中,王楚钦回顾了巴黎奥运会后的艰难时光。他表示那三个月非常难熬,曾有过无数放弃的念头,对奥运表现感到不满,尤其是在32强时输掉了外战,内心一直追问自己为什么奥林匹克历史上没有发生的事情会发生在自己身上。…