结构体内存对齐和位段(重点)!!!

2024-04-03 01:52

本文主要是介绍结构体内存对齐和位段(重点)!!!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


乐观学习,乐观生活,才能不断前进啊!!!

我的主页:optimistic_chen
我的专栏:c语言
点击主页:optimistic_chen和专栏:c语言,
创作不易,大佬们点赞鼓励下吧~

文章目录

  • 前言
  • 1.结构体类型的声明
  • 2.结构的自引用
  • 3.结构体内存对齐
    • 3.1对齐规则
    • 3.2为什么存在内存对⻬?
    • 3.3修改默认对齐数
  • 4.结构体实现位段
    • 4.1什么是位段
    • 4.2位段的内存分配
    • 4.3位段的使用
  • 完结

前言

前面的博客学习了整形和浮点型在内存中的存储,接下来学习计算结构体的大小之前博客提及过结构体C语言操作符之神秘,我们这篇博客将深入学习结构体的具体内容。

1.结构体类型的声明

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

结构的声明

   struct tag
//关键字  结构体名称{member-list;//成员列表}variable-list;//变量列表

结构的特殊声明

//匿名结构体类型
struct
{int a;char b;float c;
}x;

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

2.结构的自引用

在结构中包含⼀个类型为该结构本⾝的成员是否可以呢?

struct Node
{int a;struct Node next;
};

但是我们要计算该结构体的大小,仔细分析,其实是不⾏的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤⼩就会⽆穷的⼤,是不合理的。

正确的自引用方法:

struct Node
{int a;struct Node* next;//指针
};

3.结构体内存对齐

3.1对齐规则

1.结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对⻬数 = 编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。
3.结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

注:
VS 中默认的值为 8
Linux中?gcc?没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

练习 1

struct S
{char c1;//1char c2;//1int i;//4
};
int main()
{printf("%zd\n", sizeof(struct S));//8
}

在这里插入图片描述
在这里插入图片描述

练习 2(嵌套)

struct S1
{double d;char c;int i;
};
struct S
{char c1;struct S1 s3;double d;
};
int main()
{printf("%zd\n", sizeof(struct S));
}

在这里插入图片描述
在这里插入图片描述

3.2为什么存在内存对⻬?

  1. 平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。
    *假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。

总体来说:结构体的内存对⻬是拿空间来换取时间的做法。
那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,

或许让占⽤空间⼩的成员尽量集中在⼀起是一个不错的方法:

struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};
int main()
{printf("%zd %zd", sizeof(struct S1), sizeof(struct S2));
}

在这里插入图片描述

3.3修改默认对齐数

#pragma 这个预处理指令,可以改变编译器的默认对⻬数

#pragma pack(1)
struct S
{char c1;//1char c2;//1int i;//4
};
int main()
{printf("%zd\n", sizeof(struct S));//8
}

在这里插入图片描述

4.结构体实现位段

4.1什么是位段

  1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他类型。
  2. 位段的成员名后边有⼀个冒号和⼀个数字。
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;printf("%d", sizeof(struct S));
}

在这里插入图片描述

运行结果为什么是8呢?,这要我们了解位段的内存分配

4.2位段的内存分配

位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。

在这里插入图片描述

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

4.3位段的使用

位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。
所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。

struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
int main()
{struct A sa = { 0 };//scanf("%d", &sa._b); //这是错误的//正确的⽰范int b = 0;scanf("%d", &b);sa._b = b;return 0;
}

在这里插入图片描述

完结

本次博客到此结束
祝开心每一天~~~
最后觉得博客有帮助,可以点点关注,支持一下,期待下次博客~~~

这篇关于结构体内存对齐和位段(重点)!!!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/871621

相关文章

使用Java实现通用树形结构构建工具类

《使用Java实现通用树形结构构建工具类》这篇文章主要为大家详细介绍了如何使用Java实现通用树形结构构建工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录完整代码一、设计思想与核心功能二、核心实现原理1. 数据结构准备阶段2. 循环依赖检测算法3. 树形结构构建4. 搜索子

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

Python中如何控制小数点精度与对齐方式

《Python中如何控制小数点精度与对齐方式》在Python编程中,数据输出格式化是一个常见的需求,尤其是在涉及到小数点精度和对齐方式时,下面小编就来为大家介绍一下如何在Python中实现这些功能吧... 目录一、控制小数点精度1. 使用 round() 函数2. 使用字符串格式化二、控制对齐方式1. 使用

mysql通过frm和ibd文件恢复表_mysql5.7根据.frm和.ibd文件恢复表结构和数据

《mysql通过frm和ibd文件恢复表_mysql5.7根据.frm和.ibd文件恢复表结构和数据》文章主要介绍了如何从.frm和.ibd文件恢复MySQLInnoDB表结构和数据,需要的朋友可以参... 目录一、恢复表结构二、恢复表数据补充方法一、恢复表结构(从 .frm 文件)方法 1:使用 mysq

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

使用Navicat工具比对两个数据库所有表结构的差异案例详解

《使用Navicat工具比对两个数据库所有表结构的差异案例详解》:本文主要介绍如何使用Navicat工具对比两个数据库test_old和test_new,并生成相应的DDLSQL语句,以便将te... 目录概要案例一、如图两个数据库test_old和test_new进行比较:二、开始比较总结概要公司存在多

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

结构体和联合体的区别及说明

《结构体和联合体的区别及说明》文章主要介绍了C语言中的结构体和联合体,结构体是一种自定义的复合数据类型,可以包含多个成员,每个成员可以是不同的数据类型,联合体是一种特殊的数据结构,可以在内存中共享同一... 目录结构体和联合体的区别1. 结构体(Struct)2. 联合体(Union)3. 联合体与结构体的

PostgreSQL如何查询表结构和索引信息

《PostgreSQL如何查询表结构和索引信息》文章介绍了在PostgreSQL中查询表结构和索引信息的几种方法,包括使用`d`元命令、系统数据字典查询以及使用可视化工具DBeaver... 目录前言使用\d元命令查看表字段信息和索引信息通过系统数据字典查询表结构通过系统数据字典查询索引信息查询所有的表名可