【C/C++】了解结构体如果声明 怎么计算大小 以及一些在使用结构体时的技巧

        C语言中有很多基本的数据类型,例如int short char bool......但是在实际的开发过程中,我们会发现有的时候这些基本类型不能满足我们的需求,因此C语言中还存在有自定义的类型,例如结构体、联合体、枚举。这里我们就要介绍结构体的一些基本内容,以及结构体的内存大小到底是如何计算的。

目录

        一、结构体类型的声明

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

        三、结构体的大小计算

        1、内存对齐的规则

        2、计算结构体大小

        3、为什么存在内存对齐

        4、修改默认对齐数

        5、结构体传参


        一、结构体类型的声明

        结构体类型的声明需要用到关键字structstruct后面加上我们所要定义的结构体名称,结构体里面的成员可以是不同类型的变量,在声明结构体时定义的变量是全局变量。

struct name
{
    member—list;
}variable—list;

        在声明结构体的时候可以进行不完全的声明,例如:

struct
{
    char name[20];
    int age;
    char id[20];
}s1,s2;

这个是匿名的结构体类型,这种结构体只能使用一次。

        当结构体要包含自己为成员变量时,不可以直接包含自身,若是包含自己,结构体变量的大小就会变为无穷大,当发生自引用时,应当包含自身的结构体指针,如下:

struct Node
{
    int data;
    struct Node* next;
};

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

        结构体在初始化时,默认按照我们定义的成员变量的顺序对成员变量进行初始化,同时我们也可以通过.操作符对其中的某个成员变量进行初始化:

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

int main()
{
    struct Stu s1 = { "张三" , 20 ," 123456789"};
    struct Stu s2 = { .id = "223456789", .name = "李四" , .age = 20};
    return 0;
}

        三、结构体的大小计算

        从结构体的声明中我们可以知道结构体成员可以是不同类型的,那结构体的大小是按什么计算的,是最大的成员变量的类型吗,显然不是。

        结构体在计算大小的时候会发生内存对齐的现象,要计算一个结构体的大小就要先了解内存内存对齐的规则。

        1、内存对齐的规则

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

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

        对齐数 = 编译器默认的一个对齐数 与 该成员变量大小的 较小值

        vs中默认的指是 8

        Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

        (3)结构体总大小为最大对齐数(结构体中每个成员变量都有一个自己的对齐数,最大对齐数就是这些对齐数中最大的一个)的整数倍

        (4)如果结构体中嵌套了另外一个结构体,那么嵌套的那个结构体成员就要对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包括嵌套结构体成员中的对齐数)的整数倍

        2、计算结构体大小

        运用以上规则我们就可以来计算一个结构体占内存空间的大小了

struct S1
{
 char c1;
 int i;
 char c2;
};

struct S2
{
 char c1;
 char c2;
 int i;
};

struct S3
{
 double d;
 char c;
 int i;
};

struct S4
{
 char c1;
 struct S3 s3;
 double d;
};

        第一个成员变量的c1的类型为char它的大小为1个字节,vs默认的对齐数是8,所以char类型的对齐数是1,因此它占1个字节的空间,要对齐到结构体变量起始位置偏移量为0的地址处,int类型的大小是4个字节;因此变量i的对齐数是4,它要对齐到结构体变量起始位置偏移量4的整数倍的地址处,因为在偏移量为1、2、3处都不满足,因此这三块空间都不能使用,要把变量i放到偏移量为4的地址处,i占4个字节的大小;c2的类型是char,它要占一个字节大小,此时结构体的大小为9个字节,但是结构体的总大小要是最大对齐数的整数倍,S1的最大对齐数为4,所以还要再占用3个字节大小的空间才能实S1的大小满足最大对齐数的整数倍,因此结构体S1的大小为12个字节。

        以此类推我们可以算出S2、S3结构体的大小分别为8和16。

        我们再来计算一下S4这种结构体嵌套时的大小

        s4中第一个成员变量c1占一个字节大小,它的对齐数为1,所以它要对齐到结构体变量偏移量为0的地址处;第二个成员变量为结构体s3,s3的大小经过我们前面的计算是16,但是它的对齐数就要看s3的成员变量了,s3的成员变量中,d为double类型它的对齐数是8;c为char类型它的对齐数是1;i为int类型它的对齐数是4,当一个结构体为其他结构体的成员变量时,它的对齐数是最大对齐数,因此s3的对齐数是8,所以s3要对齐到8的整数倍的地址处,所以偏移量为1~7的地址都不能使用;d的类型为double,它的对齐数是8,它要占8个字节,此时s4的大小为1+7+16+8=32,s4的最大对齐数为8,32正好为8的整数倍,因此s4的大小就是32个字节。

        我们可以试试结果和我们计算的是不是一样的

        3、为什么存在内存对齐

        (1)平台原因(移植原因):

        不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

        (2)性能原因:

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

        总的来说:结构体的内存对齐就是用空间换取我们计算机在计算时的效率的一种做法。

        所以我们在设计结构体的时候让占用空间小的成员尽量集中在一起,我们就可以满足对齐又尽可能的节省空间。

        4、修改默认对齐数

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

#pragma pack(1)//设置默认对齐数为1
#pragma pack() //取消设置的对齐数,还原为默认

        5、结构体传参

        结构体在传参的时候尽量选择使用结构体指针的形式,因为如果我们把结构体变量当做参数传给函数时,我们都知道形数是实参的一份临时拷贝,所以我们如果把结构体当做参数传给函数时,就要用一定的时间对结构体进行拷贝并且占用一定的空间,这就会导致我们程序的效率降低;如果我们在传参时,传的是结构体的地址,在函数中我们用一个结构体指针来接收,一个指针的大小只有4/8个字节,在传递参数的时候就能节省很多的时间和空间。

        因此:结构体传参的时候,要传结构体的地址

  • 21
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值