桃猿三结义:结构、枚举、联合 上

简介: 桃猿三结义:结构、枚举、联合

文章目录

前言

古有刘备、关羽、张飞桃园三结义

现有结构、枚举、联合桃猿三结义

一、结构体

💦 什么是结构体

🔑 官方来说结构体就是一些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。说到集合,数组也是集合,但是不同的是数组只能是相同类型元素的集合

💦 结构体类型的声明

------------------------结构体声明样式------------------------

struct tag

{

  member1;

  member2;

} variable-list;

------------------------解释------------------------

▶ struct是结构体关键字

▶ tag是结构体的标签名,是自定义的

▶ struct tag就是结构体类型

▶ {}里面放的是成员列表

▶ variable-list是变量

struct Book
{
  char name[20];
  int price;
  char id[];  
}b4, b5, b6;//2、创建结构体变量
int main()
{
  //1、创建结构体变量
  struct Book b1;
  struct Book b2;
  struct Book b3;
  return 0;
}

📝 分析:

这里定义了一个结构体类型struct Book,再使用类型创建变量 (两种方法创建结构体变量):

相同的是:

▶ 它们的类型是相同的,都是struct Book

不同的是:

▶ 在main函数内创建的变量b1,b2,b3是局部变量

▶ 在main函数外创建的变量b4,b5,b6是全局变量


------------------------特殊的结构体声明样式------------------------

struct

{

  char a;

  int b;

  double c;

} s;

------------------------解释------------------------

▶ 这个结构体没有标签名tag

▶ 在声明结构体的时候,可以不完全声明

▶ 这种类型的结构体叫做匿名结构体

▶ 使用匿名结构体直接创建变量s

struct 
{
  char a;
  int b;
  double c;
} s;
struct 
{
  char a;
  int b;
  double c;
} *ps;//使用匿名结构体类型创建一个变量,这个变量是一个指针
int main()
{
  ps = &s;//?
  return 0;
}

❓❔ 两个相同的匿名结构体类型去创建变量 s 和 *ps ,问这里的 ps = &s; 是合法的吗

📐 验证:

📝分析:

▶ 在编译器看来,虽然结构体的成员是一样的,但是它会认为 s 和 *ps 是两个不同的类型,所以是非法的

▶ 所以可以试想一下,使用匿名结构体去创建变量时,只能用一次

💦 结构体的自引用

❓❔ 结构体成员包含该结构体创建的结构体变量(非指针)

struct N
{
  int a;
  struct N n;//?
};
int main()
{
  struct N n;
  return 0;
}

📝 分析:

假设这种写法是可行的,那么使用struct N去创建一个变量n,请问n的大小是多大?细想一下,你搁这卡bug呢? 这不是无限套娃吗?😵😵

📐 验证:

语法都不支持

✔ 但是结构体成员可以包含其它结构体创建的结构体变量(嵌套结构体)

struct U
{
  int b;
  int c;
};
struct N
{
  int a;
  struct U n;//?
};
int main()
{
  struct N n;
  return 0;
}

🧿 拓展


❓❔ 什么是数据结构

数据结构指的是数据在内存中存储结构


🧷 举例:如果要存储1 2 3 4 5

🔎 这里主要了解链表:

💦 结构体变量的定义和初始化

🎗 创建局部/全局结构体变量并初始化

struct Book
{
  char name[20];
  int price;
  char id[];  
}b4, b5, b6;
int main()
{
  //使用局部变量初始化
  struct Book b1 = { "CSDN", 38, "202306030033" };
  //使用全局变量初始化
  struct Book b4 = { "C语言", 40, "2451176292" };
  return 0;
}

🎗 嵌套结构体的初始化和成员访问

struct Stu
{
  char name[20];
  int age;
};
struct Book
{
  char name[20];
  int price;
  char id[20];  
  struct Stu s;
};
int main()
{
  //初始化
  struct Book b = { "C语言结构体", 40, "133927471", { "小明", 20 } };
  //访问成员
  //使用.
  printf("%s %d %s\n%s %d\n", b.name, b.price, b.id, b.s.name, b.s.age);
  //使用->
  struct Book* ps = &b;//定义一个结构体类型的指针指向b的地址 
  printf("%s %d %s\n%s %d\n", ps -> name, ps -> price, ps -> id, ps -> s.name, ps -> s.age);
  return 0;
}

💦 结构体内存对齐

❓❔ 一个结构体占多大字节呢,是不是直接把一个结构体里每个成员的类型大小加起来呢

#include<stdio.h>
struct S  
{
  int i;
  char c;
};
int main()
{
  struct S s = { 0 };
  printf("%d\n", sizeof(s));
  return 0; 
}

❓❔ 假设结构体的大小 = 结构体里每个成员大小之和,那么这里的结果是5个字节

📐 验证:

✔ 说明对于结构体是如何计算大小有它自己的规则


⚠ 这个规则就是结构体内存对齐

▶ 第1个成员在与结构体变量偏移量为0的地址处

▶ 其它成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。

 VS中默认对齐数是8; Linux没有默认对齐数,它是按照自身大小来对齐的

▶ 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍

▶ 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍


⚠ 为什么会存在内存对齐

注:大部分参考资料是这样说的(没有官方具体的说法):

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

▶ 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问

▶ 总体来说:结构体的内存对齐就是拿空间换取时间的做法

人话:

▶ 假设某些硬件平台规定只能在4的倍数地址处去访问数据,那么未来在储存时就必须存储到指 定的位置上,这样才能被访问,所以在存储数据时最好能内存对齐

▶ 对于内存对齐:b的访问只需要一次;对于非内存对齐b的访问需要二次


❓❔ 在设计结构体时,如何做到既要满足对齐,又要节省空间

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

📝 小结:

s1和s2的类型成员一模一样,但是s1和s2的大小不同。发现让占用空间小的成员尽量集中在一起有助于节省空间


❓❔ 前面有说到VS的默认对齐数是8,能不能自己调整呢

#include<stdio.h>
//默认对齐数是8
struct S1 
{
  char c1;//0
  //1-3
  int i;//4-7
  char c2;//8
  //9-11
};
//修改默认对齐数为2
#pragma pack(2)//始
struct S2 
{
  char c1;//0
  //1
  int i;//2-5
  char c2;//6
  //7
};
#pragma pack()
int main()
{
  printf("%d\n", sizeof(struct S1));//12
  printf("%d\n", sizeof(struct S2));//8
  return 0;
}

📝 小结:

结构体在对齐方式不合适的时候,那么我们可以自己调整默认对齐数


相关文章
|
弹性计算 JavaScript 前端开发
常见类型-1:空,联合,枚举
本实验将介绍TypeScript中空,联合,枚举类型的用法。
|
24天前
|
编译器
联合和枚举
联合和枚举
34 8
|
25天前
|
编译器
自定义类型:联合和枚举
自定义类型:联合和枚举
19 0
|
6月前
|
编译器 C语言 C++
结构体,枚举,联合大小的计算规则
结构体,枚举,联合大小的计算规则
41 7
|
6月前
|
存储 编译器 Linux
自定义数据类型:结构体+枚举+联合
自定义数据类型:结构体+枚举+联合
|
6月前
|
JavaScript 前端开发 编译器
TypeScript中的高级类型:联合类型、交叉类型与条件类型深入解析
【4月更文挑战第23天】探索TypeScript的高级类型。这些特性增强类型系统的灵活性,提升代码质量和维护性。
|
存储 编译器 C语言
自定义数据类型:结构体,枚举,联合
自定义数据类型:结构体,枚举,联合
|
编译器 C语言 C++
结构体、枚举、联合详解(上)
结构体、枚举、联合详解(上)
32 0
结构体、枚举、联合详解(下)
结构体、枚举、联合详解(下)
36 0
|
小程序 编译器 Linux
自定义类型详解(结构体、枚举、联合)(上)
自定义类型详解(结构体、枚举、联合)(上)