【C语言】自定义类型:结构体【结构体内存具详细】,枚举,联合

 

目录

一、结构体

1.结构的声明

 2.特殊的声明

3.结构的自引用

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

 5.结构体内存对齐(重点来了)

6.为什么会存在内存对齐 

7.修改默认对齐数

8.结构体传参

二、位段

1.什么是位段

2.位段的内存分配

3.位段的跨平台问题

三、枚举

1.枚举类型的定义

2.枚举的优点

3.枚举的使用

四、联合(共用体)


一、结构体

在此之前简单地介绍了结构体的使用初阶结构体

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

1.结构的声明

//结构体的声明
struct tag 
{member - list;//成员列表
}variable-list;//变量列表

【例如】

struct Stu
{char name[20];int age;char sex[5];char id[20];
};

 2.特殊的声明

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

如 匿名结构体类型

//匿名结构体类型
struct 
{int a;char b;float c;
}x;
struct 
{int a;char b;float c;
}a[20],*p;
//注意 不能这样写 p = &x;//编译器会把上述当成两个不同的结构体,所以是非法的

3.结构的自引用

就是结构体中含有一个类型为结构体本身的成员,如果学过数据结构的话,可能会知道链表中的node节点 就用到了结构体的自引用。

结构体的自引用方式

struct Node 
{int data;//数据域struct Node* next;//指针域,指向下一个节点
};
//另一种写法,使用类型重命名
typedef struct Node
{int data;struct Node* next;
}Node;//这样就可以 当写 struct Node 可以写为 Node

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

(1)声明类型的同时定义变量p1

//结构体变量的定义和初始化
struct Point
{int x;int y;
}p1;//声明类型的同时定义变量p1

(2)初始化,定义变量的同时赋值

struct Point p3 = {x,y};struct Stu 
{char name[15];int age;
};
struct Stu s = {"zhangsan",20};

(3)结构体嵌套初始化

struct Stu 
{int data;struct Point p;struct Node* next;
}n1 = { 10,{4,5},NULL };//结构体嵌套初始化

 5.结构体内存对齐(重点来了)

先来个问题,如何计算结构体的大小呢?这个问题就涉及了结构体内存对齐

看下方代码,并试着计算这俩个结构体分别是多大

struct S1 
{char c1;int a;char c2;
};
struct S2
{char c1;char c2;int a;
};

如果不了解结构体内存对齐的话,可能会认为这两个结构体不一样大吗?

当我们试着打印这两个结构体的大小

这时我们发现这两个结构体的大小不一样大,这是为什么呢?

看完接下来的结构体内存对齐,就明白了。

重点

要计算结构体的大小,那么我们就要掌握下方的

结构体对齐规则:

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

根据上述结构体对齐规则,我们来分析下方代码

【1】

struct S1 
{char c1;//所占大小为1int a;//所占大小为4char c2;//所占大小为1
};
//该结构体的总体大小为?

该图解具详细 

接着来分析   struct S2

【2】

struct S2
{char c1;//所占大小为1char c2;//所占大小为1int a;//所占大小为4
};//该结构体总体大小为?

图解具详细:

有了上述两个 我们接着再来看两个,巩固一下结构体内存

【3】根据结构体的内存对齐计算结构体的大小

struct S3 
{double d;char c;int i;
};

图解具详细:

【4】 计算结构体S4的大小

struct S3 
{double d;char c;int i;
};
struct S4 
{char z;struct S3 s3;double d;
};

6.为什么会存在内存对齐 

简单来说就是

  1. 平台原因 某些硬件导致的原因
  2. 性能原因 访问一些内存时,在访问为未对齐的内存,可能会访问两次内存,而访问内存对齐的内存时 ,只要访问一次即可 

所以,对于结构体的内存对齐 是 通过 空间 来换取 时间 的做法

7.修改默认对齐数

我们可能之前会看到#pragma 这个预处理指令

#pragma 可以修改默认对齐数

#include<stdio.h>
#pragma pack(1)//对齐数修改为4
struct S1 
{char c1;int a;char c2;
};
int main() 
{printf("%d",sizeof(struct S1));
}

这样经过修改默认对齐数,改为1。这样计算出来的结构体大小就是6了。

有了这个pragma,当结构体对齐方式不合适的时候我们就可以修改默认对齐数,进而来调整结构体的大小。

8.结构体传参

#include<stdio.h>
struct S 
{int data[1000];int num;
};
struct S s = { {1,2,3,4},100 };
//结构体传参
void print1(struct S s) 
{printf("%d\n",s.num);
}
//结构体地址传参
print2(struct S* s)
{printf("%d\n",s->num);
}
int main() 
{print1(s);//传结构体print2(&s);//传地址return 0;
}

注意: 因为函数传参的时候要压栈,会有时间和空间上的系统开销。而传结构体的地址的时候与会减少系统的开销。

二、位段

1.什么是位段

位段的声明和结构体是类似的,但有两点不同

  1. 位段的成员必须是int、unsigned int 、
  2. 位段的成员名后边有一个冒号和一个数字。

【例】

struct A {int a : 2;int b : 3;int c : 4;int d : 5;
};

2.位段的内存分配

位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型

位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。

位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

3.位段的跨平台问题

1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
舍弃剩余的位还是利用,这是不确定的。

三、枚举

1.枚举类型的定义

枚举就是列举

enum Color 
{RED,GREEN,BLUE
};
enum Sex 
{MALE,FEMALE,SECRET
};

enum Sex,enum Color都是枚举类型,{ }中的内容就是枚举类型可能取的值,也叫枚举常量

有些可以有值得,默认从0开始,一次递增1,定义的时候可以赋值

enum Color 
{RED=1,GREEN=2,BLUE=3
};

2.枚举的优点

1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量

3.枚举的使用

enum Color
{RED = 1,GREEN = 2,BLUE = 3
};
enum Color clr = GREEN;//这样不会出现类型的差异
clr = 5;

四、联合(共用体)

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

#include<stdio.h>
union Un
{int i;char c;
};
union Un un;
int main()
{un.i = 0x11223344;un.c = 0x55;printf("%d\n",&(un.i));printf("%d\n",&(un.c));printf("%x\n",un.i);return 0;
}

从第三个的打印结果我们可以看出来 联合体中的成员就是共用同一块内存空间。

来一个笔试题

判断当前计算机的大小端存储

//判断大小端的存储
#include<stdio.h>
int check_sys() 
{int i = 1;return *((char*)&i);// 00 00 00 01 大端// 01 00 00 00 小端//地址 低---->高
}
int main() 
{int ret = check_sys();if (ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}

上述代码就是我们通过取地址的方式来判断的

接下来我们来使用联合体来判断判断当前机器的字节序

//使用联合体来判断当前机器的字节序
#include<stdio.h>
int check_sys() 
{union {int i;char c;}un;un.i = 1;return un.c;//这里的联合体共用是同一块内存空间
}
int main() 
{int ret = check_sys();if (ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/82414.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ajax day4

1、promise链式调用 /*** 目标&#xff1a;把回调函数嵌套代码&#xff0c;改成Promise链式调用结构* 需求&#xff1a;获取默认第一个省&#xff0c;第一个市&#xff0c;第一个地区并展示在下拉菜单中*/let pname axios({url: http://hmajax.itheima.net/api/province,}).t…

21天学会C++:Day11----运算符重载

CSDN的uu们&#xff0c;大家好。这里是C入门的第十一讲。 座右铭&#xff1a;前路坎坷&#xff0c;披荆斩棘&#xff0c;扶摇直上。 博客主页&#xff1a; 姬如祎 收录专栏&#xff1a;C专题 目录 1. 知识引入 2. 运算符重载 2.1 operator<() 2.2 operator() 2.3 o…

jvm中对象创建、内存布局以及访问定位

对象创建 Java语言层面&#xff0c;创建对象通常&#xff08;例外&#xff1a;复制、反序列化&#xff09;仅仅是一个new关键字即可&#xff0c;而在虚拟机中&#xff0c;对象&#xff08;限于普通Java对象&#xff0c;不包括数组和Class对象等&#xff09;的创建又是怎样一个过…

小米华为,化干戈为玉帛!

近日来&#xff0c;手机圈又掀起了各大厂家推出新品的高潮。首先是华为Mate60的推出&#xff0c;其自研的麒麟9000S芯片瞬间点燃了国内手机市场&#xff0c;得到了国内甚至国外业界人士的认可和好评。 而近日网上盛传的小米创始人雷军的“愿意加入华为技术生态圈”的邀请&…

Redis缓存实现及其常见问题解决方案

随着互联网技术的发展&#xff0c;数据处理的速度和效率成为了衡量一个系统性能的重要指标。在众多的数据处理技术中&#xff0c;缓存技术以其出色的性能优化效果&#xff0c;成为了不可或缺的一环。而在众多的缓存技术中&#xff0c;Redis 以其出色的性能和丰富的功能&#xf…

JDK jps命令复习

之前写过jdk命令工具的博文&#xff0c;下面复习jps命令&#xff1b; jps 是 Java Process Status Tool 的简称,它的作用是为了列出所有正在运行中的 Java 虚拟机进程和相关信息&#xff1b; jps 命令参数 -q 只输出进程 ID,省略主类的名称 -m 输出虚拟机进程启动时传递…

AG35学习笔记(一):debug串口抓取模组log、debug串口测试AT指令、echo命令通过串口发送16进制数据

目录 一、概述二、抓取模组log2.1 硬件接口2.2 用户登录2.3 相关指令 三、测试AT指令3.1 查看端口3.2 进入模式 四、串口发16进制echo使用 一、概述 二、抓取模组log 在之前记录了通过USB&#xff0c;使用移远工具Qwinlog来抓取log&#xff08;3.3 抓取模组log&#xff09;。…

【Java】第一个Servlet程序

第一个Servlet程序 创建项目引入依赖手动创建必要的目录/文件编写代码打包程序部署验证程序是否正常工作 创建项目 选中maven 创建好项目后,观察左侧项目结构 引入依赖 当权代码需要使用servlet开发,而Java标准库中并没有servlet,此时就需要让maven能够把servlet的依赖获取…

子网的划分

强化计算机网络发现王道没有这一块的内容&#xff0c;导致做题稀里糊涂。于是个人调研补充。 子网划分是将一个大型IP网络划分成更小的子网&#xff0c;以实现更有效的网络管理和资源分配。 原因&#xff1a; 提高网络性能&#xff1a;子网划分可以减少广播域的大小&#xff…

成集云 | 用友NC集成旺店通ERP(旺店通主管库存)| 解决方案

源系统成集云目标系统 方案介绍 用友NC是用友NC产品的全新系列&#xff0c;是面向集团企业的世界级高端管理软件。它以“全球化集团管控、行业化解决方案、全程化电子商务、平台化应用集成”的管理业务理念而设计&#xff0c;采用J2EE架构和先进开放的集团级开发平台…

bootstrap柵格

.col-xs- 超小屏幕 手机 (<768px) .col-sm- 小屏幕 平板 (≥768px) .col-md- 中等屏幕 桌面显示器 (≥992px) .col-lg- 大屏幕 大桌面显示器 (≥1200px) 分为12个格子 -后面的1代表占12分子1也就是一份 1.中等屏幕 <div class"container-fluid a">&l…

Autojs 小游戏实践-潮玩宇宙开扭蛋

概述 最近在玩潮流宇宙&#xff0c;里面有扭蛋兔的一个玩法&#xff0c;开始有很多蛋&#xff0c;需要我们一个个点开&#xff0c;然后根据装备品质替换分解&#xff0c;潮流提供了自动开扭蛋功能&#xff0c;但是开到品质比自己装备好的时候回暂停&#xff0c;由于个人懒得看…

在Kubernetes上安装和配置Istio:逐步指南,展示如何在Kubernetes集群中安装和配置Istio服务网格

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

掌握这三大要素,轻松写出爆款软文

随着网络的快速发展&#xff0c;软文营销的趋势也在不断变化&#xff0c;做软文看似简单&#xff0c;但是想要做出成绩&#xff0c;真正吸引用户其实是有一定难度的&#xff0c;也有不少企业向媒介盒子咨询软文写作的相关话题&#xff0c;今天就让媒介盒子告诉大家&#xff0c;…

Linux查找文件内容的命令

在Linux中&#xff0c;您可以使用以下命令来查找文件内容&#xff1a; grep命令&#xff1a; grep命令用于在文件中搜索指定的文本模式&#xff0c;并将包含匹配的行打印出来。语法如下&#xff1a; grep "要查找的文本" 文件名例如&#xff0c;要在名为example.txt的…

【深度学习 | LSTM】解开LSTM的秘密:门控机制如何控制信息流

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

(超详解)堆排序+(图解)

目录&#xff1a; 1:如何建堆(两种方法) 2:两种方法建堆的时间复杂度分析与计算 3:不同类型的排序方式我们应该如何建堆 文章正式开始&#xff1a; 1&#xff1a;如何建堆 在实现堆排序之前我们必须得建堆&#xff0c;才能够实现堆排序 首先在讲解如何建堆之前让我们先来回顾一…

JDK8新特性

Lembda表达式 lembda表达式是一个简洁、可传递的匿名函数,实现了把代码块赋值给一个变量的功能 是我认为jdk1.8中最让人眼前一亮的特性&#xff08;我没用过其他函数式的语言&#xff09; 在了解表达式之前&#xff0c;我们先看两个概念 函数式接口 含有且仅含有一个抽象方法&…

CSS核心使用

CSS核心使用 box-sizingbox-shdowtext-shadowpositionwriting-mode box-sizing 定义计算一个元素的总高度和总宽度. 属性值 content-box 默认值,width 内容宽度,height内容的高度border-box 宽度和高度包含内容,内边距和边框 widthborderpadding内容宽度, heightborderpaddi…

测试进阶知识之零日攻击的发现和防御

零日攻击是指针对软件或系统中未公开&#xff08;或未被开发者知晓&#xff09;的漏洞进行的攻击。这些漏洞被称为零日漏洞&#xff0c;因为在被公开之前&#xff0c;它们对开发者或安全研究人员来说是未知的&#xff0c;所以没有足够的时间进行防御或修复。 发现零日漏洞 发…