结构体的声明和内存对齐

结构体

声明

struct Student
{
    char name; //姓名
    int age;       //年龄
    char sex[5];//性别
};                    //分号不能丢

初始化

定义结构体变量p,并且初始化

Student p={"博雅",19,"女"};

指定顺序初始化

Student s={.age=15,.name="张三"};

结构体的特殊声明

但是匿名结构体类型只能用一次,在声明时省略了标签(tag)
 

结构体自引用

可以包含一个同类型的结构体指针

typedef类型重命名,将struct node——node

也可以这样写

但是匿名的结构体类型不能实现结构体自引用是效果

结构体内存对齐

计算结构体的大小

对齐规则

  1. 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
  2. 其它成员要对齐到某个数字(对齐数)的整数倍地址

对齐数=编译器默认的对齐数 与 该成员变量大小的较小值(VS默认的值为8,Linux中gcc没有对齐数,对齐数就是成员自身的大小)

  1. 结构体的总大小是最大对齐数(结构体每一个成员都有一个对齐数)的整数倍
  2. 如果嵌套了结构体情况,嵌套的结构体成员对齐到自己成员中最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数的整数倍

练习一

struct S1
{
    char c1;
    int i;
    char c2;
};
printf("%d\n", sizeof(struct S1));//12

所以i要对齐到4的倍数的地址处,也就是4,所以i从4开始存起。

c2的大小是1个字节,VS默认是8,他两的较小值是8,所以对齐数数1,所以c2只占一个字节,所以加起来为9

c1的对齐数是1,i的对齐数是4,c2的对齐数是1,所以最大对齐数是4,

而9不是4的倍数,所以我们结构体总大小是12(4×3=12)

练习二

struct S2
{
 char c1;//1 8——1
 char c2;//1 8——1
 int i;  //4 8——4
};
printf("%d\n", sizeof(struct S2));

c1占1个字节,c2也是1,但是i的偏移量要是4的整数倍,所以必须是4开始,1+3+4=8个字节

练习三

struct S3
{
    double d;//8 8——8
    char c;//1 8 ——1
    int i;// 4 8——4
};
printf("%d\n", sizeof(struct S3));

i从4的整数倍的地址开始,所以是从12开始往下4个字节。15不是8的整数倍,所以16是结构体的总大小

练习四:结构体嵌套问题

struct S3
{
    double d;//8 8——8
    char c;//1 8 ——1
    int i;// 4 8——4
};
struct S4
{
 char c1;//1 8——1
 struct S3 s3;//成员中最大对齐数是8,从8的倍数开始,s3大小是16个字节  
 double d;//8 8——8
};
printf("%d\n", sizeof(struct S4));

因为所有最大对齐数是8,所以内存是8的倍数,32是8的倍数,所以结构体大小是32

如果结构体中有数组怎么办?

char c1[5]按照5个char存即可,也就是5个字节

int c1[5]按照5个int存即可,就是4×5=20个字节

为什么要内存对齐

提升时间,拿空间换时间的做法

修改对齐数

#pragama可以改变编译器的默认对齐数

直接访问

也可以传递地址,间接访问

结论:结构体传参的时候,尽量传结构体的地址

结构体实现位段

位段(二进制位)的声明和结构是类似的,有两个不同

专业用来节省内存的,加上数字后,规定了a占2个bit位,b占5个bit位,c占10个bit位

  1. 位段的成员必须是
  2. 位段的成员名后面有一个:和数字

结构体 位段

%zd打印无符号整数

位段的内存分配

  1. 位段成员可以是int、unsigned int、signed int或者是char等类型
  2. 尾端的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应避免使用位段
  4. 申请到的一块内存中,从左向右使用,还是从右向左使用,是不确定的,VS编译器里面是从右向左使用
  5. 剩余的空间不组织下一个成员使用的时候,是浪费还是使用呢?在VS中是浪费

首先申请1个字节(char),a占3个bit,b占4个bit,c要占5个bit但是空间不够,直接浪费,再往后申请1个字节

所以可知s占3个字节

转换成16进制

a中存10:0001010

b中存12:0001100

c中存3: 0000011

d中存4: 0000100

位段的跨平台问题

  1. int位段被当成有符号还是无符号数是不确定的
  2. 位段中最大的树木不能确定
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义

位段

不能对位段式结构的成员直接取地址

只能先放在一个变量中,然后再赋值给位段成员

  • 28
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C语言中,结构体内存对齐是一种优化手段,用于提高内存访问效率和减少内存浪费。结构体内存对齐规则是根据结构体成员的类型和顺序,以及编译器的对齐方式来确定的。 根据引用中的例子,我们可以看到结构体s1和s2的成员顺序相同,但是它们的内存对齐结果却不同。结构体s1的大小为8字节,结构体s2的大小为12字节。这是因为编译器在对齐结构体,会根据最大成员的大小来确定对齐方式。在结构体s1中,最大成员是int类型的c,大小为4字节,所以结构体s1的对齐方式是4字节对齐。而在结构体s2中,最大成员是char类型的b,大小为1字节,所以结构体s2的对齐方式是1字节对齐。 另外,根据引用的例子,我们可以看到结构体s2中嵌套了结构体s1。通过使用offsetof函数,我们可以得到结构体s2中成员a和成员c的偏移量分别为0和4字节,这表明结构体s2中的成员是按照其在结构体中的声明顺序进行排列的。同结构体s2的大小为12字节,这是因为结构体s2的对齐方式是最大成员char类型b的大小1字节。 总结起来,C语言结构体内存对齐是根据结构体成员的类型和顺序,以及编译器的对齐方式来确定的。这种对齐可以提高内存访问效率和减少内存浪费。不同的结构体可能有不同的对齐方式和大小,这取决于结构体中最大成员的大小和结构体中的成员顺序。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【C语言系列】-结构体中的内存对齐](https://blog.csdn.net/m0_64332179/article/details/122682708)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [【C语言】——结构体进阶:结构体内存对齐(超详细)](https://blog.csdn.net/luoheng1114/article/details/127106154)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值