结构体、枚举、联合详解(上)

简介: 结构体、枚举、联合详解(上)

本章内容

结构体

结构体类型的声明

结构的自引用

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

结构体内存对齐

结构体传参

结构体实现位段(位段的填充&可移植性)

枚举

枚举类型的定义

枚举的优点

枚举的使用

联合

联合类型的定义

联合的特点

联合大小的计算

结构体

数组是存放同类型数据的集合。那么结构体就可以理解为能够存放不同类型数据的集合。结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

结构体类型的声明

以下是一个结构体类型的声明:

struct student
{
  char name[20];
  int age;
  int phone[20];
};

这样我们就实现了一个student的结构体类型声明。

结构体的自引用

在 C 语言中,结构体可以包含指向自身类型的指针,从而实现自引用。这在创建包含递归或链表结构的数据类型时十分有用。

#include <stdio.h>
// 定义包含自引用的结构体
struct Node {
    int data;
    struct Node* next;
};
int main() {
    // 创建结构体变量
    struct Node n1, n2, n3;
    // 设置结构体的数据和 next 指针
    n1.data = 10;
    n2.data = 20;
    n3.data = 30;
    n1.next = &n2;
    n2.next = &n3;
    n3.next = NULL;
    // 遍历链表并输出数据
    struct Node* current = &n1;
    while (current != NULL) {
        printf("Data: %d\n", current->data);
        current = current->next;
    }
    return 0;
}

在上面的示例中,我们定义了一个包含自引用的结构体 Node,其中包含一个整数类型的 data 成员以及一个指向 Node 类型的 next 指针。

在 main 函数中,我们创建了三个结构体变量 n1、n2 和 n3,并设置它们的 data 成员为不同的值。然后,我们使用 & 运算符将 next 指针指向下一个结构体变量,最后一个结构体变量的 next 指针设置为 NULL。

通过遍历 next 指针,我们可以沿着链表遍历所有结构体,并将每个结构体的 data 值输出到控制台。

这样的结构体自引用在构建树、链表和其他递归数据结构时非常常见和有用。

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

对于这个,我们直接通过代码来更好的理解

struct Point
{
  int x;
  int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = { 3, 4 };
struct Stu 
{
  char name[15];
  int age;
};
struct Node
{
  int data;
  struct Point p;
  struct Node* next;
}n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化
int main()
{
  struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化
  struct Stu s = { "zhangsan", 20 };//初始化
  return 0;
}

这里我们直接通过调试找到监视窗口来更好的查看。

在这里稍微举一个例子,如果大家觉得定义类型过长可以通过typedef来改变。

struct student  {
  char name[20];
  int age;
  int phone[20];
};
typedef struct student Stu;
int main()
{
  Stu zhangsan = { "zhangsan",20,12345677 };
  return 0;
}

直接定义为Stu,我们就可以直接使用了。

结构体内存对齐

我们已经掌握了结构体的基本使用了。

现在我们深入讨论一个问题:计算结构体的大小。

是一个非常热门的面试考点:

结构体内存对齐

我们看这样一段代码:

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

也许会认为是6对吗?那我们来看一下运行结果吧。

如果我们把c2和i的位置换一下呢?我们直接看运行截图

为什么我们只是更改了一下顺序就发生了这样的情况,是因为结构体会以提升运行效率为目的从而浪费一些空间内存。

那么接下来我们来了解一下,结构体对齐规则

首先得掌握结构体的对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
offsetof

那么接下来我们要知道一个东西,叫做offsetof 是一个 C 语言标准库宏,用于获取结构体中成员的偏移量(即成员相对于结构体起始地址的字节偏移量)。offsetof 宏通常与指针运算符及结构体类型一起使用。

那么我们我们直接看结构体成员中的偏移量。

那么我们来根据规则计算一下

是8个大小,刚好是最大默认对齐数的两倍,所以他的大小就是8

接下来来计算它的大小

这样呢大小是9,必须是最大默认对齐数的整数倍也就是4的整数倍,那就是12了。

目录
相关文章
|
4月前
|
Linux C语言 C++
自定义类型——结构体、枚举和联合
自定义类型——结构体、枚举和联合
|
7月前
|
存储 编译器 Linux
自定义数据类型:结构体+枚举+联合
自定义数据类型:结构体+枚举+联合
|
存储 编译器 C语言
自定义数据类型:结构体,枚举,联合
自定义数据类型:结构体,枚举,联合
|
存储 编译器 Linux
自定义类型——结构体,枚举,联合
自定义类型——结构体,枚举,联合
|
编译器 C++
自定义类型:结构体,枚举,联合 (1)
自定义类型:结构体,枚举,联合 (1)
72 1
|
编译器 C++
自定义类型:结构体,枚举,联合
自定义类型:结构体,枚举,联合
|
存储 编译器 C++
自定义类型:结构体,枚举,联合 (2)
自定义类型:结构体,枚举,联合 (2)
65 0
|
存储 安全 编译器
自定义类型:结构体,枚举,联合
自定义类型:结构体,枚举,联合
112 0
结构体、枚举、联合详解(下)
结构体、枚举、联合详解(下)
44 0
|
编译器 C++
自定义类型:结构体、枚举、联合
自定义类型:结构体、枚举、联合
61 0
自定义类型:结构体、枚举、联合