【自定义类型】带你走进结构体、枚举、联合(上)

简介: 【自定义类型】带你走进结构体、枚举、联合(上)

前言

我们前面知道了C语言中有内置数据类型,也有自定义类型,详情参考【C语言进阶】深度剖析数据在内存中的存储_小王学代码的博客-CSDN博客

接下来我们详细来介绍一下自定义类型中的结构体、枚举、联合是什么,如何用?

一、结构体

结构体是一些值的集合,这些值称为成员变量,结构的每一个成员可以是不同类型的变量

1.1结构体的声明

实操代码演示:

struct Stu {//学生结构体
  char name[20];//姓名
  int age;//年龄
  char id[20];//学号
};//分号是自带的,不能丢失,
int main()
{
  struct Stu stu = { "name",12,"123456" };
  return 0;
}

1.2 特殊声明和结构自引用

在声明结构的时候,可以用不完全声明,这个时候是可以的,但是一定要在分号之前设置全局变量,否则无法找到这个结构体(匿名结构体)

演示:

struct {
  int age;
  char name[20];
}x;
//是可以这样没有标签的,但是前提是创建x这样的全局变量,否则之后就找不到了
//因为是匿名结构体
struct {
  int age;
  char name[20];
}a[10],*p;
int main()
{
  scanf("%s", x.name);
  printf("%s", x.name);
  p = &x;
//非法的,因为p和x不是指向同一个结构体类型的,虽然成员变量一样,但是实际上类型是不同的
  return 0;
}

接下来我们思考一下,上面代码的第一个和第二个结构体是否一样,是否可以 p=&x

图示:

上面说明了,编译器会把上面两个结构体的声明当成完全不同的两个类型,所以是非法的

1.4结构体的自引用

结构体的自引用多用于数据结构那边,比如顺序表、链表等,接下来我们介绍一下,声明叫做结构体的自引用

结构体的自引用,顾名思义在这个结构体内部使用该结构体类型的成员变量

图示:

代码演示:

//正确的自引用方式   有*号
struct Node
{
int data;
struct Node* next;
};
//错误的方式   没有*号
struct Node
{
int data;
struct Node next;
};

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

上面我们介绍了如何声明,接下来我们来看看如何使用结构体,对其初始化和定义

代码演示:

struct Point {
  int x;
  int y;
}p1;      //声明类型的同时进行定义变量p1  这个是全局变量
struct Point p2;  //这也是定义结构体变量,在结构体外面,函数外面,这也是全局变量
//初始化,定义变量的同时进行赋初值
struct Point p3 = { 1,2 };
struct Stu {
  char name[20];//姓名
  int age;//年龄
};
struct Stu s = { "why",20 };//初始化,这也是全局变量
struct Node {
  int date;
  struct Point p;
  struct Node* next;
}n1={10,{1,2},NULL};  //结构体嵌套初始化
struct Node n2 = { 10,{1,2},NULL };//结构体嵌套初始化

剩下的如何赋值,如何初始化:

struct Point
{
  int x;
  int y;
}p1 = {10, 20};
struct Point p2 = {0,0};
struct S
{
  int num;
  char ch;
  struct Point p;
  float d;
};
int main()
{
  struct Point p3 = {1,2};
  struct S s = { 100, 'w', {2,5}, 3.14f};
//这些属于基础的部分,赋值需要使用大括号,内部如果有其他类型的结构体,也要进行使用大括号
  struct S s2 = {.d=1.2f, .p.x=3,.p.y=5, .ch = 'q', .num=200};
  //创建一个变量之后,可以使用 .d=1.2f 这样的形式进行赋值,可以不用考虑结构体成员变量的顺序
  printf("%d %c %d %d %f\n", s.num, s.ch, s.p.x, s.p.y, s.d);
  printf("%d %c %d %d %f\n", s2.num, s2.ch, s2.p.x, s2.p.y, s2.d);
  return 0;
}

1.6 结构体内存对齐

上文。我们已经基本了解了结构体的基本使用。

我们都知道数据类型都会在内存中占据空间,我们来深入了解计算一下结构体的大小

这是一个很重要的知识点:结构体内存对齐

什么叫做内存对齐呢?是如何对齐呢?有以下几条规则

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

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

   对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值

  VS中默认的值为8

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

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

代码演示:

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;
}

图示分析讲解:

为什么要存在内存对齐呢?

解释:

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

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

2. 性能原因:

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

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

总之:结构体的内存对齐,是拿空间换取时间的做法!!!

所以在以后的结构体的设计中,我们尽量要满足对齐,又要节省空间,所以我们应该

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

相关文章
|
9月前
|
存储 编译器 Linux
自定义类型——结构体,枚举,联合
自定义类型——结构体,枚举,联合
|
10月前
|
编译器 Linux C++
学C的第三十天【自定义类型:结构体、枚举、联合】-1
1 . 结构体 (1). 结构体的基础知识: 结构是一些值的集合,这些值称为成员变量。 结构的每个成员可以是不同类型的变量。
|
10月前
|
存储
学C的第三十天【自定义类型:结构体、枚举、联合】-2
(7). 修改默认对齐数: 结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。 使用 #pragma 预处理指令,修改默认对齐数
|
5月前
|
编译器 C++
自定义类型:结构体,枚举,联合
自定义类型:结构体,枚举,联合
|
5月前
|
编译器 C++
自定义类型:结构体,枚举,联合 (1)
自定义类型:结构体,枚举,联合 (1)
45 1
|
5月前
|
存储 编译器 C++
自定义类型:结构体,枚举,联合 (2)
自定义类型:结构体,枚举,联合 (2)
36 0
|
5月前
|
存储 安全 编译器
自定义类型:结构体,枚举,联合
自定义类型:结构体,枚举,联合
85 0
|
编译器 C++
【学习笔记之我要C】自定义类型详解(结构体+枚举+联合)
【学习笔记之我要C】自定义类型详解(结构体+枚举+联合)
274 0
|
10月前
|
存储 编译器 C语言
【自定义类型】带你走进结构体、枚举、联合(下)
【自定义类型】带你走进结构体、枚举、联合(下)
|
10月前
|
编译器 Linux C++
详解自定义类型:结构体,枚举,联合(上)
详解自定义类型:结构体,枚举,联合(上)