C/C++结构体最详细的讲解

转载自知乎:https://zhuanlan.zhihu.com/p/611772031

1.定义结构体

法一(推荐,写法简单)

struct Student{
 string m_Name;
 int m_Age;
 Student()=default;
 Student(string name,int age):m_Name(name),age(m_Age){}
};

一般定义结构体和类时,如果不写关于构造函数的任何东西,结构体和类会生成默认构造函数。所以在定义结构体时,不写有参构造函数,系统会为结构体自动生成默认构造函数,这样可以直接定义结构体变量并可以不进行初始化,但是如果在定义结构体时写了有参构造函数,有参构造函数会覆盖默认构造函数,那样在定义结构体变量时,就必须对结构体变量进行初始化。

如果结构体中写了有参构造函数,还想要在不进行变量初始化的情况下定义一个结构体变量,那么需要在结构体中显式声明默认构造函数(结构体名()=default;)。

写结构体的默认构造函数的优势在于可以创建结构体变量时不进行变量的初始化,写有参构造函数可以在创建结构体变量时只初始化结构体的部分参数。

声明结构体变量 struct Student stu{“ybc”,26}; struct可写可不写

访问结构体成员变量 stu.m_Name;

法二

struct Student{
 string m_Name;
 int m_Age; 
 Student()=default;
 Student(string name,int age):m_Name(name),age(m_Age){}
}stu;

stu是一个变量,可以直接访问m_Name,如stu.m_Name;

如果以后不再定义结构体Student的变量,可以省略Student,写成

struct{
 string m_Name;
 int m_Age; 
}stu;

stu同样是结构体的变量,缺点不可以写结构体的构造函数了 wuwu~

法三(推荐)

typedf struct Student{
 string m_Name;
 int m_Age;
 Student()=default;
 Student(string name,int age):m_Name(name),age(m_Age){} 
}stu;

stu是一个结构体类型,stu=struct Student

因为typedef经常用于给数据类型起别名,可以理解为

typedef struct Student stu; stu就成为了struct Student的别名,可以与struct Student执行相同的操作。

使用stu方式,

stu Stu{“ybc”,26};
Stu.m_Name;

2.结构体内存对齐

下面声明三个具有相同数据成员但成员变量的排列顺序不同的结构体 char_short_long、long_short_char和char_long_short。

//声明结构体char_short_long
struct 
{
char c;
short s;
long l;
}char_short_long;
//声明结构体long_short_char
struct
{
long l;
short s;
char c;
}long_short_char;
//声明结构体char_long_short
struct 
{
char c;
long l;
short s;
}char_long_short;

在32位机中,char占1个字节,short占2个字节,long占4个字节。

通过执行程序可知,结构体char_short_long占8个字节,结构体long_short_char占8个字节,结构体char_long_short占12个字节

我们注意到,1 byte (char)+ 2 bytes (short)+ 4 bytes (long) = 7 bytes,既不是8 bytes,也不是12bytes。

所以,结构体成员变量的放置顺序影响着结构体所占的内存空间的大小。一个结构体变量所占内存的大小不一定等于其成员变量所占空间之和。如果一个用户程序或者操作系统(比如uC/OS-II)中存在大量结构体变量时,这种内存占用必须要进行优化,也就是说,结构体内部成员变量的排列次序是有讲究的。

结构体成员变量到底是如何存放的呢?

在没有#pragma pack宏的情况下:

  1. 原则1 结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
  2. 原则2 结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
  3. 原则3 结构体作为成员时,结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素时,那么b应该从8的整数倍地址处开始存储,因为sizeof(double) = 8 bytes)

解析下char_long_short为什么占12个字节

结构体的内存分布如下图所示

结构体char_long_short 内存分布图

首先,1244972能被1整除,所以char_long_short.c放在1244972处没有问题(其实,就char型成员变量自身来说,其放在任何地址单元处都没有问题),根据原则1,在之后的1244973~1244975中都没有能被4(因为sizeof(long)=4bytes)整除的,1244976能被4整除,所以char_long_short.l应该放在1244976处,那么同理,最后一个.s(sizeof(short)=2 bytes)是应该放在1244980处。

是不是这样就结束了?不是,还有原则2。根据原则2的要求,char_long_short这个结构体所占的空间大小应该是其占内存空间最大的成员变量的大小的整数倍。如果我们到此就结束了,那么char_long_short所占的内存空间是1244972~1244981共计10bytes,不符合原则2,所以,必须在最后补齐2个 bytes(1244982~1244983)。

至此,一个结构体的内存布局完成了。

3.联合/联合体(union)

union的定义方式与结构体类似,参考结构体即可。

结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。

结构体占用的内存大于等于所有成员占用的内存的总和(结构体存在内存对齐问题),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。

 

 

posted @ 2024-03-08 16:55  Hazy_star  阅读(0)  评论(0编辑  收藏  举报