自定义类型:结构体2.0(初阶+进阶)+位段+枚举+联合(中)

简介: 自定义类型:结构体2.0(初阶+进阶)+位段+枚举+联合(中)

1.8结构体变量的定义和初始化✈️

struct S
{
  int a;
  char c;
}s1;
struct S s3;//这样定义也是全局变量
struct B
{
  float f;
  struct S s;
};
  int main()//在这里定义的全是局部变量
{
  //int arr[10] = {1,2,3};
  //int a = 0;//有了值才有确定性
    //结构体怎么初始化呢?
  struct S s2 = {100, 'q'};
  struct B sb = { 3.14f, {200, 'w'}};//结构体的嵌套初始化正常来说要按照顺序,
//不按照顺序初始化
    struct S s3 = {.c = 'r', .a = 2000};//指定顺序   
  printf("%f,%d,%c\n", sb.f, sb.s.a, sb.s.c);//结合体成员的访问,后面两个是因为是嵌套了一个结构体
   return 0;
}


另一个例子:

struct S
{
  char c;
  char arr[10];
  int* p;
}s1, s2;
struct S s3;
struct B
{
  int a;
  struct S s;
  double d;
}sb1, sb2;
struct B sb3;
int main()
{
  struct S s4 = {'c', "zhangsan", NULL};
  int a = 10;
  struct B sb4 = { 100, {'q', "lisi", &a}, 3.14};
  return 0;
}


1.9结构体内存对齐🚁

该如何计算结构体的大小:结构体内存对齐🐔🏀👖

这也是一个特别热门的考点: 结构体内存对齐

#include<stdio.h>
struct S1
{
  int a;
  char c;
};
struct S2
{
  char c1;
  int a;
  char c2;
};
struct S3
{
  char c1;
  int a;
  char c2;
  char c3;
}; 
int main()
{
  printf("%d\n", sizeof(struct S1));
  printf("%d\n", sizeof(struct S2));
  printf("%d\n", sizeof(struct S3));
  return 0;
}

运行结果:

74b676b30c224a87af630dd8371b8c23.png

按以往的知识来说,从S1到S3的值分别推测为5,6,7,可是结果却不是这样的,并且S3比S2还多了一个成员变量,但是它们的大小却是相等的

8b338e5bc3de4ec5929acfa63c958848.png

为什么呢?计算结构体,需要用到以下规则  

f47343d342e34f58a22a1d0f17b7e047.png

接下来,需要详细说明一下结果是如何求出来的


1.9.1先放int,再放char📣

struct S1
{
  int a;
  char c;
};
int main()
{
    printf("%d\n", sizeof(struct S1));
  return 0;
}


20c85a768cd445c591caef1d2bc2ecec.png

1.9.2调换顺序:先放char,再放int🔈

那么我们讲char c和int a的位置调换一下,结果又会不会不一样呢?

struct S1
{
  char c;
  int a;
};
int main()
{
    printf("%d\n", sizeof(struct S2));
  return 0;
}

fb7a6379c8bb40e09bde5af159351546.png

c和a之间真的浪费了3个字节的空间吗?

#include<stdio.h>
#include <stddef.h>
struct S
{
  char c1;
  int a;
};
int main(){
  struct S s = {0};
  printf("%d\n", offsetof(struct S, c1));//0
  printf("%d\n", offsetof(struct S, a));//4
  return 0;
}


🔎如何计算两个结构体变量的偏移量差距🔎

方法1:取地址计算,4和8之间差4

image.png

可以发现它们之间差了4个字节

方法2:offsetof函数

使用之前要引用一下头文件

image.png

可以看到是返回成员变量的偏移量

image.png

执行效果:

74f78a64d2364c33a40dd11b1263af5a.png

1.9.3先放两个char,再放一个int🔏

struct S2
{
  char c1;
  char c2;
  int i;
};
int main()
{
  printf("%d\n", sizeof(struct S2));
  return 0;
}


22f7cbff3215493892eae123571911af.png

e59f93b8e68e4cdc9c76ab6dad4d944d.png

1.9.4先放一个double,再放一个char,最后放一个int💡

struct S3
{
  double d;
  char c;
  int i;
};
int main()
{ 
printf("%d\n", sizeof(struct S3));
  return 0;
}


执行效果:

69b5a929da224899b620276e7b9e31ef.png

ce979b6573034539a92a7610b7428503.png

1.9.5嵌套结构体计算 🔦

struct S3
{
  double d;
  char c;
  int i;
};
struct S4
{
  char c1;
  struct S3 s3;
  double d;
};
int main()
{   
   //计算嵌套结构体总结构体的大小
  printf("%d\n", sizeof(struct S4));
  return 0;
}

image.png

1.9.6回到整个题目🔨

#include<stdio.h>
struct S1
{
  int a;
  char c;
};
struct S2
{
  char c1;
  int a;
  char c2;
};
struct S3
{
  char c1;
  int a;
  char c2;
  char c3;
}; 
int main()
{
  printf("%d\n", sizeof(struct S1));
  printf("%d\n", sizeof(struct S2));
  printf("%d\n", sizeof(struct S3));
  return 0;
}


image.png

为什么存在内存对齐?

大部分的参考资料都是如是说的:


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

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特

定类型的数据,否则抛出硬件异常。

2. 性能原因

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。

原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访

问。

取决于机器字长

如果是32位机器,一次读写读32bit,那就是4byte

599965d0edd14e969329a8ba72892d29.png

总结:

1.对于一个int 类型,若是按照内存对齐来存储,处理器只需要访存一次就可以读取完4个字节

2.若没有按照内存对其来读取,如上图所示,就需要访问内存两次才能读取出一个完整的int 类型变量

总体来说:

结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:

让占用空间小的成员尽量集中在一起

#include<stdio.h>
struct S1
{
  char c1;
  int i;
  char c2;
};
struct S2
{
  char c1;
  char c2;
  int i;
};
int main()
{
  printf("%d\n", sizeof(struct S1));
  printf("%d\n", sizeof(struct S2));
  return 0;
}

70b2dfa6bfa6465faa1d9e3e00ac4dae.png

调整一下顺序就可以减少空间的浪费


2.0修改默认对齐数

之前我们见过了 #pragma这个预处理指令,这里我们再次使用,可以改变我们的默认对齐数。

#pragma pack(1)//设置默认对齐数为1
struct S
{
  char c1;//1 1 1
  int i;  //4 1 1
  char c2;//1 1 1
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
  printf("%d\n", sizeof(struct S));
  return 0;
}


b3c8c565591246c1812c34e7b6f558dd.png

因为它们每一个对齐数都是1,所以只要是1的倍数的偏移量位置,它们都可以存放,所以就不会有空余的内存

结论:

结构在对齐方式不合适的时候,我么可以自己更改默认对齐数。(我们不想对齐的时候就把对齐数设置成1)

相关文章
|
8月前
|
编译器
自定义类型:结构体2.0(初阶+进阶)+位段+枚举+联合(上)
自定义类型:结构体2.0(初阶+进阶)+位段+枚举+联合(上)
|
10月前
|
编译器 C语言
C语言自定义类型 — 结构体、位段、枚举、联合
本期主要对通讯录三篇博客文章进行补充 通讯录文章:通讯录系列文章 对结构体进行详细介绍,其次讲解位段、枚举、联合体
71 0
|
8月前
|
存储 编译器 Linux
详解结构体、位段、枚举、联合类型【C语言/进阶】
详解结构体、位段、枚举、联合类型【C语言/进阶】
61 0
|
1月前
|
存储 开发框架 编译器
C语言进阶—自定义类型:结构体,枚举,联合
C语言进阶—自定义类型:结构体,枚举,联合
|
1月前
|
存储 开发框架 .NET
【C语言进阶】自定义类型详解(结构体、枚举、联合)
【C语言进阶】自定义类型详解(结构体、枚举、联合)
|
7月前
|
存储 编译器 Linux
C语言进阶-自定义类型:结构体、枚举、联合(1)
C语言进阶-自定义类型:结构体、枚举、联合
52 0
|
7月前
|
存储 编译器 C语言
C语言进阶-自定义类型:结构体、枚举、联合(2)
C语言进阶-自定义类型:结构体、枚举、联合
36 0
|
7月前
|
编译器 C语言
C语言进阶,第4节-自定义类型详解(结构体+枚举+联合)
C语言进阶,第4节-自定义类型详解(结构体+枚举+联合)
|
8月前
|
存储 编译器
自定义类型:结构体2.0(初阶+进阶)+位段+枚举+联合(下)
自定义类型:结构体2.0(初阶+进阶)+位段+枚举+联合(下)
|
10月前
|
编译器 Linux C语言
C语言进阶-自定义类型:结构体、枚举、联合(上)
C语言进阶-自定义类型:结构体、枚举、联合(上)
51 0