[C语言]结构体、位段、枚举常量、联合体

2024-03-24 09:36

本文主要是介绍[C语言]结构体、位段、枚举常量、联合体,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

结构体

结构体的使用方法

结构体所占用的大小

位段

位段的使用方法

位段所占用的大小

枚举常量

枚举常量的使用方法

枚举常量的优势

联合体

联合体的使用方法

结构体

结构体的使用方法

结构体是一些值的集合,我们可以定义一个结构体,里面可以包含不同类型的值例如定义一个学生我们可以这样使用结构体

那我们要输入一个学生的信息应该如何呢?

我们写一段这样的代码

#include <stdio.h>
struct stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}s2 = { "Hardworker2",28,"女","20240323002" };
void main()
{struct stu s1 = { "Hardworker1",18,"男","20240323001" };printf("%s %d %s %s\n", s1.name, s1.age, s1.sex, s1.id);printf("%s %d %s %s\n", s2.name, s2.age, s2.sex, s2.id);
}

这段代码的运行结果如下

可以看到在结构体后面跟一个变量也可以完成结构体的初始化,或者在主函数再次声明变量也可也初始化,这两种有什么区别呢?区别就是在结构体后初始化的变量如果在主函数之上就是一个全局变量,而在主函数内的变量为局部变量。

另外结构体声明也可以调用结构体,如

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

在结构体内调用其他的结构体也是可以的,那我们能不能在结构体内部调用自己呢?

用这种方式是不行的,因为如果再次调用自己的话,就会再次开辟一次内存,内存会一直递归开辟,直到程序崩溃,但是我们可以用指针的方式调用自己,需要注意的是,我们需要的是一部分装我们想要的数据,另外一部分装下一个需要相同类型结构体的地址,如下

为了方便理解,笔者写了一段代码便于理解如何一部分装值一部分装地址

#include <stdio.h>struct Node
{int value;struct Node* next;
};void print_chain(struct Node* init)
{while (init->next){printf("%d", init->value);init = init->next;if (!(init->next)){printf("%d", init->value);}}
}
void main()
{struct Node f = { 1,NULL };struct Node e = { 2,&f };struct Node d = { 3,&e };struct Node c = { 4,&d };struct Node b = { 5,&c };struct Node a = { 6,&b };print_chain(&a);
}

我们看到依次打印为654321

结构体所占用的大小

如下程序,结构体占用的大小是多少呢

按理来说char占用一个字节int占用四个字节那么一共就是六个字节,但是当我们输出sizeof(S1)却发现结果为12个字节,那结构体在内存中是怎么进行存放的呢?

我们来看一下结构体给与内存的规则是怎么样的

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

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8

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

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

因此第一个char地址为偏移量为0的地方即为0

int类型的地址为某个数字(对齐数)的整数倍的地址处,编译器默认的一个对齐数 与 该成员大小的较小值 既为4的整数倍,因此1、2、3都不可以为int的地址,只有4存放,而int占4个字节大小,所以4-7均为int的地址

下一个char按照第二个规则继续存放,既存放到8的地址,而为什么没有结束呢?

按照3的规矩,总大小为最大对齐数的整数倍,所以我们必须再开辟三个字节让结构体的大小为12才可以结束,我们可以看看图片更为直观的理解结构体的大小占用

位段

位段的使用方法

位段的声明和结构是类似的,有两个不同:

1.位段的成员必须是 char int、unsigned int 或signed int 。

2.位段的成员名后边有一个冒号和一个数字。

如下

其实后面的冒号数代表的就是分配给其所占的比特数大小,那么位段的大小为多少呢?

位段所占用的大小

对于上面的位段我们来测试一下此位段所占用的大小

我们发现占用的大小为4个字节,但是按理来说,如果是比特数大小其字节数应该6<(2+16+32)/8<7 那为什么输出的结果为8呢?其实原因在于,对于一个整型变量,如果超出了四个字节之后,位段结构体会为其再开辟四个字节,但是问题又来了,c中存放的32个比特的数据是在后四个字节里面还是一部分再前四个字节里面一部分在后四个字节里面呢?

让我们给这三个参数赋值来看看答案如何

#include <stdio.h>struct test 
{int a : 2;int b : 16;int c : 32;
}s;void main()
{struct test d = { 1,30,20 };printf("%d", sizeof(s));
}

我们调用d的内存发现其存放的方式如下

其实我们仔细分析一下,a作为占2比特的变量,结构体分给其第一个字节后两位,接着剩下的还是在第一个定义的int的四个字节内,所以依次剩下的6个比特以及12个比特分给了b,如所示区域,但是当分配给c的时候,c占用32个比特,第一个int所占的四个字节无法接纳那么大的内存,所以开辟了一段新的空间给它,但是上一个没用完的空间是不分配给c的,所以我们最终看到系统分配内存方式如下

枚举常量

枚举常量的使用方法

枚举顾名思义就是一一列举。 把可能的取值一一列举。

比如我们现实生活中: 一周的星期一到星期日是有限的7天,可以一一列举。

性别有:男、女、保密,也可以一一列举。

月份有12个月,也可以一一列举

这里就可以使用枚举了。

枚举的代码如下所示

enum Day//星期
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};
enum Sex//性别
{MALE,FEMALE,SECRET
};
enum Color//颜色
{RED,GREEN,BLUE
};

上面所定义的量都称之为枚举常量,是不可以进行修改的常量。

拿颜色举例,就是赋值了

RED大小为0的枚举常量

GREEN大小为1的枚举常量

BLUE大小为2的枚举常量依次累加

如果想要定义RED为4

直接在enum的时候赋值RED=4就好

但是接下来的数据也会变成

GREEN = 5 ;BLUE =6

枚举常量的优势

枚举的优点:

1. 增加代码的可读性和可维护性

2. 和#define定义的标识符比较枚举有类型检查,更加严谨。

3. 防止了命名污染(封装)

4. 便于调试

5. 使用方便,一次可以定义多个常量

联合体

联合体的使用方法

联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。 比如:

#include <stdio.h
//联合类型的声明
union Un
{char c;int i;
};
//联合变量的定义
union Un un;
void main()
{
//计算连个变量的大小printf("%d\n", sizeof(un));
}

联合体,顾名思义就是里面定义的内容共用同一块内存区域,联合的大小至少是最大成员的大小。 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

当每次赋值的时候,联合体内的内存都会被重新赋值

这篇关于[C语言]结构体、位段、枚举常量、联合体的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/841196

相关文章

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

C++11作用域枚举(Scoped Enums)的实现示例

《C++11作用域枚举(ScopedEnums)的实现示例》枚举类型是一种非常实用的工具,C++11标准引入了作用域枚举,也称为强类型枚举,本文主要介绍了C++11作用域枚举(ScopedEnums... 目录一、引言二、传统枚举类型的局限性2.1 命名空间污染2.2 整型提升问题2.3 类型转换问题三、C

MySQL中的索引结构和分类实战案例详解

《MySQL中的索引结构和分类实战案例详解》本文详解MySQL索引结构与分类,涵盖B树、B+树、哈希及全文索引,分析其原理与优劣势,并结合实战案例探讨创建、管理及优化技巧,助力提升查询性能,感兴趣的朋... 目录一、索引概述1.1 索引的定义与作用1.2 索引的基本原理二、索引结构详解2.1 B树索引2.2

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)

如何使用Maven创建web目录结构

《如何使用Maven创建web目录结构》:本文主要介绍如何使用Maven创建web目录结构的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录创建web工程第一步第二步第三步第四步第五步第六步第七步总结创建web工程第一步js通过Maven骨架创pytho

Python循环结构全面解析

《Python循环结构全面解析》循环中的代码会执行特定的次数,或者是执行到特定条件成立时结束循环,或者是针对某一集合中的所有项目都执行一次,这篇文章给大家介绍Python循环结构解析,感兴趣的朋友跟随... 目录for-in循环while循环循环控制语句break语句continue语句else子句嵌套的循

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

Go语言如何判断两张图片的相似度

《Go语言如何判断两张图片的相似度》这篇文章主要为大家详细介绍了Go语言如何中实现判断两张图片的相似度的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 在介绍技术细节前,我们先来看看图片对比在哪些场景下可以用得到:图片去重:自动删除重复图片,为存储空间"瘦身"。想象你是一个