深度解析结构体

目录

结构体的声明

结构体的特殊声明

结构体初始化

结构体自引用

结构体内存对齐

修改默认对系数

结构体传参

结构体实现位段

位段的声明:

位段的内存分配


结构体的声明

结构体声明一般常用的两种。一种是带有struct和创建结构体类型的名字,例如struct stu s1

另一种是用typedef转换,把结构体声明转换成另一名字,例如typedef struct { }st;

命名结构体时就不必那么麻烦,直接st s2即可。

struct stu
{
    char name[29];
    int age;
};

typedef struct 
{
    char name[39];
    int age;
}st;

int main()
{
    struct stu s1 = { "zhangsan", 19 };//按照结构体成员顺序初始化
    struct stu s1 = { .age = 39, .name = "zhangsan"};//按照指定顺序初始化
    printf("%s ", s1.name);
    printf("%d\n", s1.age);
    st s2 = { "lisi", 30 };
    printf("%s ", s2.name);
    printf("%d\n", s2.age);
    return 0;
}

结构体的特殊声明

匿名结构体类型

一个匿名结构体只能用一次

匿名结构体与匿名结构体间的类型不一样,即使一模一样。

结构体初始化

结构体初始化也分为两种,一种是按照结构体成员顺序初始化,另一种按照指定顺序初始化。

struct stu s1 = { "zhangsan", 19 };//按照结构体成员顺序初始化
struct stu s1 = { .age = 39, .name = "zhangsan"};//按照指定顺序初始化

结构体自引用

这里就联系上链表了,链表就是可以通过结构体自引用实现。

struct Node

{
        int data;

        struct Node* next;

};

结构体内存对齐

根据内存对齐规则可以计算结构体的大小。

结构体对齐规则:

1 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。

2 其他成员变量要对齐到某个数字(对齐数)的整数倍处。

对齐数 = 编译器默认的一个对齐数,即该成员变量大小的较小值(不同编译器的对齐数不同,vs默认8, Linux中gcc没有默认对齐数,对齐数就是成员自身大小)

3 结构体总大小为最大对齐数的整数倍。

4 嵌套结构体的情况下,嵌套的结构体成语对齐自己成员中最大的对齐数的整数倍

例1

char类型c1对齐数为1,int i对齐数为4,故4+4 = 8,char c2 对齐数 1 ,8+1 = 9;又根据 3 结构体总大小为最大对齐数的整数倍。最大对齐数为4,最小整数倍为12

同理,int i, 跳到4处,char c1 char c2 对齐数均为1,跳到6处,最大对齐数为4,最小整数倍为8

例2,结构体嵌套

略微修改s1和s2,s1中,char c1占1,int i 对齐数为4,故从4开始,4+4 = 8, double c2 对齐数为8,可从8开始,8+8 = 16,满足条件3,16是8的倍数,故内存大小为16。

s2,struct s1 s的最大对齐数为8(条件4),占16,int i 对齐数为4,满足倍数关系,16+4  =20,char c1, char c2对齐数均为1,20+2 = 22,又根据条件3,最大对齐数为8,最小倍数大小24。 

修改默认对系数

使用#pragma pack(n)    n为修改后的对齐数。

对齐数为1,也就是说无论是几都是1的倍数,char c1 大小为1, int i 为4,char c2 为1,加在一起为6。如果没有修改对齐数就会和上面的一样,大小为12。

#pragma pack(),默认对齐数

结构体传参

两种传参方式,一种是直接传结构体,另一种是传递结构体的地址。

使用首选后者,即地址传参。函数传参时,参数需要压栈,会有空间和系统上的开销。

如果传递的结构体过大时,参数压栈的系统开销变大就会导致性能下降。

函数压栈是指在调用函数时,函数的相关信息(如参数、局部变量、返回地址等)被保存在栈上,以便函数执行完毕后能够正确返回到调用点继续执行。函数压栈通常发生在以下情况下:

  1. 函数调用:当一个函数被调用时,当前函数的执行状态(如参数、局部变量、返回地址等)会被保存在栈上,这样新函数就可以开始执行。

  2. 局部变量:函数中声明的局部变量通常存储在栈上,因此在函数调用时,这些局部变量的内存空间也会被分配在栈上。

  3. 参数传递:函数调用时,参数值通常通过栈来传递给被调用函数。

  4. 返回地址:在函数调用时,返回地址会被压入栈中,以便函数执行完毕后能够返回到调用点继续执行。

总之,函数压栈是为了保存函数执行时所需的状态信息,以便在函数执行完毕后能够正确返回到调用点。这是计算机程序中实现函数调用和返回的重要机制之一。

结构体实现位段

位段的声明:

1.位段的成员必须时int 、unsigned int或signed int,在C99中位段成员的类型也可以选择其他类型。

2.位段的成员后面必须有一个冒号和一个数字(数字表示成员的位段大小,单位时bit)

struct A
{
    int a : 2;
    int b : 5;
    int c : 10;
    int d : 30;
};

struct A中的位段成员加在一起的大小为47bit,一个int类型大小为4个字节,32个bit位,47>32,分配了两个整形的大小,即8个字节。

位段的内存分配

位段的空间上时按照需要以四个字节(int)或一个字节(char)的方式开辟。

注意,位段是不跨平台的,注重可移植的程序应该避免使用位段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值