C# 认识结构体,结构体的对齐规则及使用(详解!详解!)


我们知道数组可以存放很多数据,但是一个数组只能存放一种同类型的数据,为了解决这个问题,就出现了结构体,结构体可以存放多种类型的数据。

结构体的声明及特殊声明

struct tag  // tag是结构体类型名,根据自己的需要自定义
{
    number-list;//结构体的成员列表
}variable-list; //variable-list表示变量列表,可有可无

示例:

struct Student
{
    //成员变量
    char name[20];
    int age;
    float score;
};

可以看到结构体确实可以存放许多不同类型的数据

对结构体进行特殊声明

  • 也叫不完全声明(不写结构体类型名)

示例:

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

像上面这样的结构体类型只能用一次(一般用于较小的工程中)

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

以上面的示例为例子,对结构体变量进行创建和初始化:

#include <stdio.h>
struct Student
{
    //成员变量
    char name[20];
    int age;
    float score;
}s3,s4;//在这里也可以创建结构体变量
int main()
{
    //创建结构体变量
    struct Student s1 = 0;//struct Student 代表s1的类型,说明了s1是结构体变量
    //对结构体变量进行初始化
    s1 = {"lisi",19,90}; //这是按着成员变量的顺序进行初始化
    struct Student s2 = 0// s2.age = 100;//只对s2这个变量的成绩进行初始化
    //下面这条语句是不按成员变量的顺序进行初始化
    s2 = {.score = 98,.name = "wangwu",.age = 20};
    return 0;
}

访问结构体的成员

访问结构体成员,需要用到两个操作符,第一个操作符是“ . ”操作符,第二个操作符是“->”操作符

点(“ . ”)操作符:结构体变量 . 成员变量名
箭头(“ -> ”)操作符:结构体变量的地址 -> 成员变量名

示例:

#include <stdio.h>
struct Student
{
    char name[20];
    int age;
}s1,s2;
int main()
{
    s1 = {"lisi",19};
    s2 = {"wangwu",20};
    //运用点操作符访问
    printf("%s %d\n",s1.name,s2.age);
    //运通箭头操作符访问
    struct Student *ps2 = &s2;
    printf("%s %d\n",ps2 -> name,ps2 -> age);
    //用数组进行访问
    struct Student s[2] = {s1,s2};
    for(int i = 0; i < 2;i++)
    {
      printf("%s %d\n",s[i].name,s[i].age);
    }
    return 0;
}
结构体的自引用

用一个例子来讲解,如:定义一个链表,链表中有多个节点,如何对每个链表的节点进行访问

先用画图板给大家画一个链表:

在这里插入图片描述

struct Node
{
    int data;
    struct Node next;// next代表下一个节点
};

上面的代码其实是错误的,一个结构体中包含一个同类型的结构体,会使结构体成员变量的大小变得无穷大,一直访问下去,sizeof(struct Node)是多少是不可知的,所以这样的结构体自引用是不合理的。

正确的自引用:

struct Node
{
    int data;//数据域
    struct Node *next;//指针域
};

我们可以通过存放下一个结点的地址,来找到下一个节点,这样的sizeof(struct Node)是可知的。

结构体内存对齐(计算结构体大小)

定义一个结构体,计算该结构体的大小:

#include <stdio.h>
struct S1
{
    char c1;
    char c2;
    int n;
}s1;
int main()
{
    s1 = {0};
   //打印结构体的大小
   printf("%zd\n",sizeof(struct S1));
   return 0;
}

大家是否会认为该结构体的大小是:1+1+4=6
其实输出的结果是:8
出现这样的结果是因为,结构体成员在内存中是存在对齐现象的。

offsetof函数

在讲对齐现象之前,先给大家介绍一下offsetof函数,该函数是计算结构体成员相较于结构体变量起始位置的偏移量,可通过该函数来计算结构体的大小
示例:计算上面代码中结构体成员的偏移量

#include <stdio.h>
#include <stddef.h>//offsetof函数的头文件
struct S1
{
    char c1;
    char c2;
    int n;
};
int main()
{
   //打印结构体成员的偏移量
   //打印的是无符号整数,所以用%zd打印
   printf("%zd\n",offsetof(struct S1,c1));//偏移量是0
   printf("%zd\n",offsetof(struct S1,c2));//偏移量是1
   printf("%zd\n",offsetof(struct S1,n));//偏移量是4
   return 0;
}

输出的结果是:0 1 4

通过上面offsetof函数算出的c1,c2,n的偏移量,可得出结构体struct S1的大小
画一块内存帮助大家理解
在这里插入图片描述
可知,得到结构体变量的偏移量可算出结构体的大小

回到结构体内存对齐
结构体成员在内存中的对齐是有规则的,规则如下:

  1. 结构体的第1个成员要对齐到起始位置偏移量为0的地址处
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
    对齐数 = 编译器默认的一个对齐数与该成员变量的大小比较,谁小谁就是对齐数
    vs中默认的对齐数是8
    linux中gcc没有默认对齐数,对齐数就是成员自身的大小
  3. 结构体的总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的那个)的整数倍
  4. 如果嵌套了结构体情况,嵌套的结构体成员对齐到自己成员中最大的对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍

举例:
还是用刚刚struct S1的代码进行举例,该结构体的大小是8

#include <stdio.h>
struct S1
{
    char c1;
    char c2;
    int n;
}s1;
int main()
{
    s1 = {0};
   //打印结构体的大小
   printf("%zd\n",sizeof(struct S1));
   return 0;
}

按规则来,第一条规则:结构体的第1个成员要对齐到起始位置偏移量为0的地址处

在这里插入图片描述
第二条规则:其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处,对齐数 = 编译器默认的一个对齐数与该成员变量的大小比较,谁小谁就是对齐数,vs中默认的对齐数是8。
在这里插入图片描述

第三条规则:结构体的总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的那个)的整数倍
在这里插入图片描述
第四条规则:如果嵌套了结构体情况,嵌套的结构体成员对齐到自己成员中最大的对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍

//先定义一个嵌套结构体
struct S1
{
    char c1;
    char c2;
    int n;
};
struct S2
{
    char c3;
    struct S1 s1;
};

struct S2中嵌套了struct S1,由刚刚可知,struct S1的大小为8,还是一样借助画图板来分析struct S2的大小
在这里插入图片描述
要注意的是,当得出的结构体大小不是最大对齐数的整数倍时,需要继续增加字节数,达到整数倍的即可

关于结构体的知识就讲到这,还有不懂的或者错误的欢迎提出,一起加油!

  • 31
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C语言结构体的字节对齐规则是根据结构体中的成员变量的类型和对齐值来确定的。对于一个结构体来说,它的自身对齐值是结构体中所有成员变量中的最大对齐值。而结构体的有效对齐值是结构体的自身对齐值和操作系统的对齐值中的较小值。 在给定的例子中,结构体A中包含了一个short型变量b、一个int型变量c和一个char型变量a。根据引用\[2\]中的解释,short型变量占用2字节,int型变量占用4字节,char型变量占用1字节。因此,结构体A的自身对齐值为4字节。 根据引用\[3\]中的解释,结构体A的有效对齐值是结构体的自身对齐值和操作系统的对齐值中的较小值。在这个例子中,操作系统的对齐值也是4字节。所以,结构体A的有效对齐值也是4字节。 因此,根据C语言结构体的字节对齐规则结构体A中的成员变量a和b要组成4个字节,以便与成员变量c的4个字节对齐。由于成员变量a只占用1个字节,所以在a和b之间会有一个字节的空隙。 总结起来,C语言结构体的字节对齐规则是根据结构体中的成员变量的类型和对齐值来确定的,以保证结构体对齐要求和内存的高效利用。 #### 引用[.reference_title] - *1* *2* *3* [C语言结构体——关于内存字节对齐图文详解](https://blog.csdn.net/qq_62932195/article/details/125821103)[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^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值