【C语言】自定义类型:结构体、枚举、联合(上)

简介: 【C语言】自定义类型:结构体、枚举、联合(上)

前言:

本篇重点为结构体内存对齐与位段的概念,该知识点是一个非常热门的考点,相信本篇文章会给你带来收获,博主会持续更新C语言的进阶知识,感兴趣的读者可以关注博主动态🌟🌟🌟

一、结构体

(1)结构体的特殊声明

我们知道结构体的声明大概为下面的方式:

struct tag
{
    member - list;//成员变量列表
}variable - list;//变量列表
struct Stu
{
    char name[20];//名字
    int age;//年龄
    char sex[5];//性别
    char id[20];//学号
}; //分号不能丢

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

比如:

//匿名结构体类型
struct
{
    int a;
    char b;
    float c;
}x;
struct
{
    int a;
    char b;
    float c;
}a[20], * p;

上面的两个结构在声明时省略了结构体标签tag,这种被称为匿名结构体

我们发现两个结构体的成员变量是完全一致的,那么我们可不可以认为上面的两个结构体是相同类型的呢?

//在上面代码的基础上,下面的代码合法么?

p = &x;

答案是不能,因此该代码也不合法,原因在于编译器会把上面的两个声明当成完全不同的两个类型。

(2)结构体的自引用

这里的自引用指的是指针,即结构体中可以包含该结构体类型的指针,如:

struct Node
{
    int data;
    struct Node* next;
};

而不可以是:

struct Node
{
    int data;
    struct Node next;
};
//err

原因在于其自身类型大小无法计算

即sizeof(struct Node)是多少? 你会发现他会无穷的一直计算下去。

刚才学习了匿名结构体,那么我们看下面的代码是否可行:

//代码3
typedef struct
{
  int data;
  Node* next;
}Node;//err

答案是不行的,原因在于该匿名结构体省略了结构体标签tag,

也就是说,Node的声明在Node* next之后,即还没有创建Node,就要使用。

(3)结构体嵌套初始化

struct Point
{
  int x;
  int y;
}p1;
struct Node
{
  int data;
  struct Point p;
  struct Node* next;
}n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化
struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化

像上面这种结构体中包含了另一个结构体初始化的情况就叫做结构体嵌套初始化。

(4)结构体内存对齐

接下来我们来学习如何计算结构体的大小,其涉及到了结构体内存对齐的概念,也是本篇最为重要的内容。

首先给出两个结构体,你认为他的大小为多少?

//练习1
struct S1
{
  char c1;
  int i;
  char c2;
};//6?
//练习2
struct S2
{
  char c1;
  char c2;
  int i;
};//6?

在学习结构体内存对齐前, 你可能认为两个都为6,但是实际上为12和8

也就是说结构体中有的成员变量之间存在空隙,浪费了一定的空间。

那么空隙如何计算呢?

宏:offsetof( type, member )(函数不能传参类型,但宏可以)

使用此宏需引用头文件stddef.h,该宏可以计算结构体成员相较于结构体起始位置的偏移量。

如图:

我们发现结构体大小的计算不是那么简单,那么结构体大小遵从什么样的规则呢?

结构体的对齐规则:

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

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

这里数组比较特殊,它的大小为数组元素类型的大小,而不是整个数组的大小,比如char c[5],他的大小为1,若默认对齐数为8,则对齐数取较小为1。

VS中默认的值为8。gcc编译器无默认对齐数,对齐数就是结构体成员自身大小。

学习了如何计算,那我们来尝试两道题:

//练习1
struct S1
{
    double d;
    char c;
    int i;
};
//练习2-结构体嵌套问题
struct S2
{
    char c1;
    struct S1 s1;
    double d;
};

答案:S1大小16,偏移量分别为0、8、12;

          S2大小32,偏移量分别为0、8、24。

练习1分析:

练习2分析:

目录
相关文章
|
8月前
|
C语言
【C语言程序设计——循环程序设计】枚举法换硬币(头歌实践教学平台习题)【合集】
本文档介绍了编程任务的详细内容,旨在运用枚举法求解硬币等额 - 循环控制语句(`for`、`while`)及跳转语句(`break`、`continue`)的使用。 - 循环嵌套语句的基本概念和应用,如双重`for`循环、`while`嵌套等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台将对编写的代码进行测试,并给出预期输出结果。 5. **通关代码**:提供完整的代码示例,帮助理解并完成任务。 6. **测试结果**:展示代码运行后的实际输出,验证正确性。 文档结构清晰,逐步引导读者掌握循环结构与嵌套的应用,最终实现硬币兑换的程序设计。
125 19
|
8月前
|
C语言
【C语言程序设计——枚举】得到 3 种不同颜色的球的可能取法(头歌实践教学平台习题)【合集】
本关任务要求从红、黄、蓝、白、黑五种颜色的球中,每次取出3个不同颜色的球,列举所有可能的排列情况。通过定义枚举类型和使用嵌套循环语句实现。枚举类型用于表示球的颜色,循环语句用于生成并输出所有符合条件的排列 编程要求:在指定区域内补充代码,确保输出格式正确且完整。测试说明:平台将验证代码输出是否与预期一致,包括每种排列的具体顺序和总数。 示例输出: ``` Output: 1 red yellow blue 2 red yellow white ... 60 black white blue total: 60 ```
171 4
|
9月前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
668 14
|
9月前
|
存储 编译器 C语言
【C语言】结构体详解 -《探索C语言的 “小宇宙” 》
结构体通过`struct`关键字定义。定义结构体时,需要指定结构体的名称以及结构体内部的成员变量。
436 10
|
10月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
834 13
|
10月前
|
存储 数据建模 程序员
C 语言结构体 —— 数据封装的利器
C语言结构体是一种用户自定义的数据类型,用于将不同类型的数据组合在一起,形成一个整体。它支持数据封装,便于管理和传递复杂数据,是程序设计中的重要工具。
|
10月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
194 4
|
存储 C语言
【C语言】 条件操作符 -- 逗号表达式 -- []下标访问操作符,()函数调用操作符 -- 常见关键字 -- 指针 -- 结构体
【C语言】 条件操作符 -- 逗号表达式 -- []下标访问操作符,()函数调用操作符 -- 常见关键字 -- 指针 -- 结构体
【C语言】——define和指针与结构体初识
【C语言】——define和指针与结构体初识
|
存储 算法 Linux
初识C语言【补】——指针、结构体
初识C语言【补】——指针、结构体
138 0
初识C语言【补】——指针、结构体