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

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

本章内容

结构体

结构体类型的声明

结构的自引用

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

结构体内存对齐

结构体传参

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

枚举

枚举类型的定义

枚举的优点

枚举的使用

联合

联合类型的定义

联合的特点

联合大小的计算

结构体

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

结构体类型的声明

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

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了。

目录
相关文章
|
Oracle druid 关系型数据库
阿里Druid连接池的坑。。
Druid的坑 当查询数据库的Clob转换为Oracle Clob类型的时候。
672 0
|
运维 文件存储 对象存储
【运维知识进阶篇】用阿里云部署kod可道云网盘(配置Redis+MySQL+NAS+OSS)(三)
【运维知识进阶篇】用阿里云部署kod可道云网盘(配置Redis+MySQL+NAS+OSS)(三)
521 0
|
安全 Java
Jprofile解析dump文件使用详解(一)
Jprofile解析dump文件使用详解(一)
877 0
Jprofile解析dump文件使用详解(一)
|
Java 分布式数据库 数据库
软件各种系统架构图
原文:软件各种系统架构图 https://blog.csdn.net/everythingss/article/details/78749247     该技术架构图是本人根据多年企业技术架构经验而制定,是企业技术的总架构图,希望对CTO们有所借鉴。
8270 0
|
10月前
|
弹性计算 运维 Serverless
超值选择:阿里云Elasticsearch Serverless在企业数据检索与分析中的高性能与灵活性
本文介绍了阿里云Elasticsearch Serverless服务的高性价比与高度弹性灵活性。
410 8
|
11月前
|
前端开发 Java 应用服务中间件
Tomcat和Nginx的资源路径映射
Tomcat和Nginx的资源路径映射
289 1
|
9月前
|
存储 缓存 Java
Python高性能编程:五种核心优化技术的原理与Python代码
Python在高性能应用场景中常因执行速度不及C、C++等编译型语言而受质疑,但通过合理利用标准库的优化特性,如`__slots__`机制、列表推导式、`@lru_cache`装饰器和生成器等,可以显著提升代码效率。本文详细介绍了这些实用的性能优化技术,帮助开发者在不牺牲代码质量的前提下提高程序性能。实验数据表明,这些优化方法能在内存使用和计算效率方面带来显著改进,适用于大规模数据处理、递归计算等场景。
235 5
Python高性能编程:五种核心优化技术的原理与Python代码
|
11月前
|
传感器 存储 物联网
在物联网(IoT)快速发展的今天,C语言作为物联网开发中的关键工具,以其高效、灵活、可移植的特点
在物联网(IoT)快速发展的今天,C语言作为物联网开发中的关键工具,以其高效、灵活、可移植的特点,广泛应用于嵌入式系统开发、通信协议实现及后端服务构建等领域,成为推动物联网技术进步的重要力量。
373 1
虚拟机安装Linux系统的网络配置
该博客文章提供了解决虚拟机中Linux系统网络问题的多种方法,包括重置网络服务、修改网络配置文件、使用不同网络模式等,以确保虚拟机能够成功连接到网络。
虚拟机安装Linux系统的网络配置