结构体,联合体与位段

简介: 结构体,联合体与位段

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

1.1 为什么需要结构体内存对齐?

原因1:平台原因

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

比如,当一个平台要取一个整型数据时只能在地址为4的倍数的位置取得,那么这时就需要内存对齐,否则无法访问到该整型数据。

原因2: 性能原因

数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐内存,处理器需要作两次内存访问;而对齐的内存访问仅需一次。比如说下面这个整形i,我们想在一个32位机器访问它,一次读取四个字节,就需要两次才能访问完整的i,如果对齐了就只需要一次访问即可.

1.2 结构体内存对齐规则

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

1.算出每个成员的对齐数(VS的默认对齐数是8,对齐数就是本身成员变量)

2.将每个元素对齐到较小值的整数倍下面.

3.通过最大对齐数知道大小最后的大小,比如说这个结构体的大小一定要是最大对齐数的整数倍,最大对齐数就是上面求出的较小值中的最大值.(是8),此时大小是16,正好是8的倍数,大多数情况下,此时的大小并不是最大对齐数的整数倍,这个时候我们只需补齐即可.

1.3 设计结构体的技巧

其实在我们设计结构体的时候,如果结构体成员的顺序设计得合理的话,是可以避免不必要的内存消耗的。

两个结构体的成员变量相同,但是成员变量的顺序不同,可能就会出现结构体的大小不同的情况:

struct S1
{
  char a;
  char b;
  int c;
};
struct S2
{
  char a;
  int c;
  char b;
};

这个时候我们发现,结构体S1占用的是8个字节,而结构体S2就占用了12个字节,所以我们在设计结构体的时候需要将占用内存小的元素放在一块儿,可以避免内存的浪费.

1.4 修改默认对齐数

在VS2022中,我们可以自由的修改默认对齐数来满足我们的内存需求.

#pragma pack()

只需在pack中填入你设置的默认对齐数即可.

2.联合体

2.1 什么是联合体

在C语言中,存在这样一个类型,可以让多个变量共用一块内存,这就是联合体.

使用场景:在某个变量不使用的时候可以使用其他变量.

缺点:在使用其他变量的时候会改变内存中的数据

union 联合名
{
成员表
};

2.2 联合体的创建方式(和结构体相似)

1.先创建模板,再定义变量

// 创建联合体模板union u
union u
{
    int i;
    char c;
};
// 使用该联合体模板创建两个变量a, b
union u a,b;

2.创建模板时同时定义变量

// 创建联合体模板union u的同时定义两个变量a、b
union u
{
    int i;
    char c;
};

3.匿名联合体

union
{
    int i;
    char c;
}a,b;

4.typedef

typedef union u
{
    int i;
    char c;
}U;
U a = {1,'a'};

2.3 初始化

U a;
a.i = 10;
U b = a;                /* 1、把一个联合初始化为另一个同类型的联合; */
U c = {20};               /* 2、初始化联合的第一个成员; */
U d = {.i = 30};                /* 3、根据C99标准,使用指定初始化器。 */

2.4 应用:判断机器的大小端

typedef union u
{
    int i;
    char c;
}U;
U u;
u.i = 0x12345678
//打印c,如果是78就是小端,如果是12就是大端

3.位段

3.1 什么是位段

位段是通过结构体来实现的一种以位(bit位)为单位的数据存储结构,它可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作.这样可以节省空间.

注:位段里面的成员只能有整形家族来组成,否则会报错.位段后面分配的比特位不能超过原本类型的大小.

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

3.2 位段内存计算

我们以这个结构体举例

struct S
{
   char a:3;
   char b:4;
   char c:5;
   char d:4;
};
int main()
{
  struct S s = {0};
  printf("%d\n", sizeof(s));
  return 0;
}

实际上结果是3个字节,我们画图来看一下,这样相对直接使用结构体来说还是节省了不少空间的.

3.3 位段的跨平台问题

1.int型位段成员会被当成有符号数还是无符号数是不确定的

2.位段中最大位数目是不确定的(在16位机器上int型最大为16,而在32为机器上int型最大为3.32,如若写成27,那么16位机器就会出问题)

4.位段的成员在内存中到底是从左向右分配,还是从右向左分配尚未定义

5.当一个结构体包含两个位段,第二个位段比较大,无法容纳于第一个位段剩余的位时, 是舍弃剩余的位还是利用,是不确定的。

综上所述:位段是不跨平台的

相关文章
|
1月前
|
机器学习/深度学习 C语言
结构体与共用体2
结构体与共用体2
20 0
|
1月前
结构体与共用体3
结构体与共用体3
19 0
|
1月前
|
存储 编译器 C语言
超全超详细的C语言结构体、位段、枚举、联合体详解
超全超详细的C语言结构体、位段、枚举、联合体详解
|
1月前
|
存储 编译器 C语言
自定义类型:结构体(自引用、内存对齐、位段(位域))
自定义类型:结构体(自引用、内存对齐、位段(位域))
|
1月前
|
存储 编译器 C语言
结构体的内存对齐与位段
当我们描述一个人的年龄时我们可以使用,int age = 18;但是如果我们要描述一个人呢?很显然我们无法仅靠一个age就实现对一个人的描述,所以就有了结构体,在结构体中我们可以包含多种类型的数据,这样就可以实现对一个人的描述比如身高、爱好、体重等等
|
6月前
|
编译器 C++
结构体、枚举、位段、联合体详解
结构体、枚举、位段、联合体详解
52 0
|
8月前
|
存储 编译器 C语言
C/C++之自定义类型(结构体,位段,联合体,枚举)详解
C/C++之自定义类型(结构体,位段,联合体,枚举)详解
57 0
|
10月前
|
存储 编译器 C语言
结构体对齐+联合体+位段
结构体对齐+联合体+位段
|
10月前
|
存储 C语言
自定义类型——位段,枚举,共用体(联合体)
✅<1>主页:C语言的前男友 📃<2>知识讲解:结构体,声明,变量初始化,结构体内存对齐 🔥<3>创作者:C语言的前男友 ☂️<4>开发环境:Visual Studio 2022 💬<5>前言:今天继续把剩下的自定义类型拉出来说一说。
自定义类型——位段,枚举,共用体(联合体)
结构体位段问题
什么是位段 位段的详细解释 位段其实也是一种结构体的类型 1.位段的成员是 int ,short int unsigned int , signed int , short , char 类型 2.位段的成员名后有一个冒号和一个数字 看一个例子: