【落羽的落羽 C语言篇】自定义类型——结构体

在这里插入图片描述

文章目录

  • 一、结构体
    • 1. 结构体类型的概念和声明
    • 2. 结构体变量的创建和初始化
    • 3. 结构体成员的访问
      • 3.1 直接访问
      • 3.2 间接访问
    • 4. 结构体的内存对齐
      • 4.1 内存对齐的规则
      • 4.2 内存对齐的原因
      • 4.3 修改默认对齐数
    • 5. 结构体传参
    • 6. 结构体实现位段

在C语言中,已经提供了一些基本的内置类型,如int、char、short等等。但有时,我们的一些数据不能只从是简单地归结于这些单一的数据类型。所以,我们就需要 自定义类型。常见的自定义类型有三种: 结构体、联合体、枚举。今天我们首先来介绍一下最重要的:结构体。

一、结构体

1. 结构体类型的概念和声明

结构是一些值的集合,这些值被称为成员变量。结构的每个成员可以是不同类型的变量,如指针、字符、数组,甚至是又一个结构体。

结构体的声明为:

struct name
{
    member;
    member;
    ......
};

其中,struct是C语言中的一个关键字,name是结构体变量的名字,里面的member是成员。

举个栗子,我想从名字、年龄、身高描述一个人,写成结构体类型就是:

struct human
{
    char name[20];//名字
    int age;//年龄
    float length;//身高
};
//一定记得最后的分号不能丢
//一个话题外的小知识点:一个汉字相当于两个字符的大小

除此之外,还有一种结构体的匿名声明,在声明结构体类型时不写出名字,这个类型的变量只能在声明后紧接着创建,结构体变量创建好后它的声明直接销毁,也就是只能用一次。在实际应用中,这种特殊的声明方式其实完全没必要使用,大家仅做了解就好~

2. 结构体变量的创建和初始化

创建好一个结构体类型后,我们就可以创建这种类型的结构体变量了,创建的方式有两种:

//方式一:在结构体类型的声明后直接创建,如a1
struct human
{
    char name[20];
    int age;
    float length;
}a1;

//方式二:结构体声明后在下面的语句创建,如a2
struct human a2;

创建后,当然也要存入数据了,结构体变量的初始化方法为:

struct human
{
    char name[20];
    int age;
    float length;
}a1;
//默认情况下,数据是按照声明的结构体成员顺序初始化的:
a1 = {"zhangsan", 18, 180.0};
struct human a2 = {"lisi", 20, 178.0};

//也可以指定顺序初始化,要在成员名前加. :
struct human a3 = {.length=175.5, .name="wangwu", .age=30};

3. 结构体成员的访问

3.1 直接访问

结构体成员的直接访问是通过点操作符.完成的,如下:

struct human
{
    char name[20];
    int age;
    float length;
}a1 = {"zhangsan", 18, 180.0};

printf("年龄是:%d\n", a1.age);
printf("身高是:%f\n", a1.length);

在这里插入图片描述

3.2 间接访问

结构体类型也是一种类型,创建了一种结构体类型,自然也有指向这种结构体变量的指针类型了:

struct human
{
    char name[20];
    int age;
    float length;
}a1;

struct human* p1 = &a1;

有时候我们得到的不是一个结构体变量,而是一个指向结构体的指针,我们需要用这个指针找到结构体的成员,这就是结构体成员的间接访问,要用到->操作符:

struct human
{
    char name[20];
    int age;
    float length;
}a1 = {"zhangsan", 18, 180.0};

struct human* p1 = &a1;

printf("年龄是:%d\n", p1->age);
printf("身高是:%f\n", p1->length);

在这里插入图片描述

结果也是对的

4. 结构体的内存对齐

4.1 内存对齐的规则

知道了结构体的基本使用方法,我们更深入一点,学习一下计算结构体的大小,这涉及到结构体内存对齐。

基本规则为:

  • 结构体的第一个成员,对齐到和结构体变量起始位置相同的地址(偏移量为0)
  • 其他成员变量,对齐到偏移量为对齐数的整数倍的地址处
  • 每个成员的对齐数是,编译器的默认对齐数该成员变量的大小两者中的较小值VS的默认对齐数是8;Linux中gcc没有默认对齐数,对齐数即是成员自身大小。
  • 结构体的总大小是:所有成员的对齐数中的最大值的整数倍

我们来分析两个结构体就理解了(以下代码都在VS下运行):

例子1:

struct s1
{
    char c;
    int i;
    double d;
};
  1. 第一个成员c的大小是1,占据内存开始偏移量为0~1的地址。1和默认对齐数8相比,较小值是1,所以name的对齐数是1。
  2. 第二个成员i的大小是4,和默认对齐数8相比,较小值是4,所以i的对齐数是4,要存在偏移量是4的整数倍的地址处。从1往后数,是4,所以i存在偏移量4~8的地址处。
  3. 第三个成员d的大小是8,和默认对齐数8相比,较小值是8,所以d的对齐数是8,要存在偏移量是8的整数倍的地址处。8正好是,所以id存在偏移量8~16的地址处。
  4. 而所有成员的对齐数中最大对齐数是8,结构体的总大小是8的倍数。16正好是,
    所以这个结构体的大小是16。
    在这里插入图片描述在这里插入图片描述
    例子2:
struct s2
{
    char c1;
    int i;
    char c2;
}
  1. 第一个成员c1的大小是1,占据内存开始偏移量为0~1的地址。1和默认对齐数8相比,较小值是1,所以c1的对齐数是1。
  2. 第二个成员i的大小是4,和默认对齐数8相比,较小值是4,所以i的对齐数是4,要存在偏移量是4的整数倍的地址处。从1往后数,是4,所以i存在偏移量4~8的地址处。
  3. 第三个成员c2的大小是1,和默认对齐数8相比,较小值是1,所以c2的对齐数是1,要存在偏移量是1的整数倍的地址处。8正好是,所以id存在偏移量8~9的地址处。
  4. 而所有成员的对齐数中最大对齐数是4,结构体的总大小是4的倍数。从9往后数,是12,
    所以这个结构体的大小是12。

在这里插入图片描述在这里插入图片描述

4.2 内存对齐的原因

原因了解一下就好:

  1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:数据机构应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需一次访问。假设一个处理器总是从内存中取八个字节,则地址必须是八的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成八的倍数,那么就可以用一个内存操作来读或写值了。否则,我们需要执行两次内存访问,因为对象可能被分别放在两个八字节内存块中。

换句话说,结构体的内存对齐,是用空间节省时间的做法。

所以,我们在设计结构体时,为了尽量节省空间,应该做到:让占用空间小的成员尽量集中在一起。比如:

struct s1
{
   char c1;
   int i;
   char c2;
};

struct s2
{
    char c1;
    char c2;
    int i;
}

s2所占空间就比s1要小。

4.3 修改默认对齐数

#pragma这个预处理指令,可以改变编译器的默认对齐数
用法是:#pragma pack(n),n是我们希望改成的默认对齐数;使用完这个我们修改的对齐数,写下#pragma pack(),就能取消设置的对齐数,还原为编译器默认的。

这样,在结构体内存对齐方式不合适的时候,我们可以自己设计对齐方式。

在这里插入图片描述

5. 结构体传参

结构体类型也是可以作为函数参数传递的,但传的时候,我们有传值和传址两种选择。大多数情况下,都建议选择传递结构体的地址(指针)
原因是:函数传参的时候,参数需要压栈,会有时间和空间上的系统开销。如果传递一个结构体时这个结构体过大,参数压栈的系统开销比较大,可能会导致性能的下降。

6. 结构体实现位段

位段也是一种数据结构,今天做一个初步的了解就好,它的声明和使用和结构体极为相似。只有两点不同:

  • 位段的成员必须是int、unsigned int、signed int,在C99中位段成员的类型也可以选择其他类型。
  • 位段的成员名后边要写一个冒号和一个不超过32的数字。

每个成员名后的数字代表着给这个成员变量申请的空间大小(单位是比特)。
比如:

struct s1
{
  int i:10;
  char c1:2;
  char c2:4;
};

但要知道的是:

  • 位段上的空间是按照需要以4个字节或1个字节的方式来开辟的
  • 位段涉及很多不确定因素:int位段被当做有符号数还是无符号数是不确定的、位段中最大的数目不能确定(16位机器上不能超过16,32位机器上不能超过32)、位段中成员在内存中是从左向右分配还是从右向左分配的是没有标准定义的、一个位段成员用剩下的比特位是给下一个位段成员用还是浪费是不确定的。综上所述,位段涉及很多不确定因素,它是不跨平台的,注重可移植性的程序应当避免使用位段。而在企业中,使用位段,一般会根据不同的环境,写出不同的代码。
  • 访问位段成员时,不能对位段成员使用&操作符,也不能用scanf函数直接给位段成员输入值,只能是先输入放在一个变量中,再赋值给位段成员。

在这里插入图片描述

本篇完,感谢阅读

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CSS盒子模型(溢出隐藏,块级元素和行级元素的居中对齐,元素样式重置)

overflow:值 规定了内容溢出元素框时所发生的事情 visible:内容不会被修剪,会显示在元素框之外,默认值 overflow: visible; hidden:内容会被修剪,溢出内容不可见 overflow: hidden; scroll:内…

Spark-Streaming集成Kafka

Spark Streaming集成Kafka是生产上最多的方式,其中集成Kafka 0.10是较为简单的,即:Kafka分区和Spark分区之间是1:1的对应关系,以及对偏移量和元数据的访问。与高版本的Kafka Consumer API 集成时做了一些调整,下面我们…

【Python】pandas库---数据分析

大学毕业那年,你成了社会底层群众里,受教育程度最高的一批人。 前言 这是我自己学习Python的第四篇博客总结。后期我会继续把Python学习笔记开源至博客上。 上一期笔记有关Python的NumPy数据分析,没看过的同学可以去看看:【Pyt…

数字IC后端设计实现篇之TSMC 12nm TCD cell(Dummy TCD Cell)应该怎么加?

TSMC 12nm A72项目我们需要按照foundary的要求提前在floorplan阶段加好TCD Cell。这个cell是用来做工艺校准的。这个dummy TCD Cell也可以等后续Calibre 插dummy自动插。但咱们项目要求提前在floorplan阶段就先预先规划好位置。 TSCM12nm 1P9M的metal stack结构图如下图所示。…

LiteFlow决策系统的策略模式,顺序、最坏、投票、权重

个人博客:无奈何杨(wnhyang) 个人语雀:wnhyang 共享语雀:在线知识共享 Github:wnhyang - Overview 想必大家都有听过或做过职业和性格测试吧,尤其是现在的毕业生,在投了简历之后经…

YashanDB 23.2 YAC -单库多实例架构多活共享集群安装部署指南

一、概述 1.1 文档目标 ​ 本说明旨在指导技术人员在 CentOS 7 x86_64 操作系统上完成崖山数据库企业版 23.2 的共享集群安装与部署。通过系统架构、集群拓扑及部署需求的精确描述,帮助读者在开始安装前对崖山数据库的架构形成清晰认识。本文以高效、稳定、安全为…

某科技局国产服务器PVE虚拟化技术文档

环境介绍 硬件配置 服务器品牌:黄河 型号:Huanghe 2280 V2 Cpu型号:kunpeng-920 磁盘信息 :480SSD * 2 ,4T*4 网卡:板载四口千兆 如下表 四台服务器同等型号配置,均做单节点虚拟化,数据保护采用底层r…

Gin-vue-admin(4):项目创建前端一级页面和二级页面

目录 创建一级页面创建二级页面 创建一级页面 view目录下新建一个my&#xff0c;Index.vue <template></template><script> export default {name:My, } </script><script setup> import {ref} from vue const myNameref("name") &…

Ubuntu下的tcl/tk编程快速入门

一、Tcl/Tk 简介 接口介绍 https://www.tutorialspoint.com/tcl-tk/tcl_tk_quick_guide.htm GUI Canvas接口 https://www.tutorialspoint.com/tcl-tk/tk_canvas_widgets.htm tcl语言 https://www.tcl-lang.org/ 官方教程 https://www.tcl.tk/man/tcl8.5/tutorial/tcltutoria…

数字化审计咨询服务,企业转型数字化审计的必要条件

人工智能、云计算、大数据、物联网等新兴技术的快速发展&#xff0c;为企业的数字化转型提供了强大的技术支持。这些技术逐渐被应用到企业运营管理的方方面面&#xff0c;推动了企业内部审计工作的变革。随着数字化转型的深化和信息技术的不断发展&#xff0c;数字化审计将成为…

【QT常用技术讲解】发送POST包(两种方式:阻塞方式及非阻塞方式)

前言 http/https(应用层)协议是广泛使用的网络通信协议。在很多与第三方API对接的场景中&#xff0c;通常是通过http/https协议完成&#xff0c;比如API对接时&#xff0c;通常要通过POST包获取access_token进行鉴权&#xff0c;然后再进行数据交互&#xff08;本篇也包含有对接…

重撸设计模式--代理模式

文章目录 定义UML图代理模式主要有以下几种常见类型&#xff1a;代理模式涉及的主要角色有&#xff1a;C 代码示例 定义 代理模式&#xff08;Proxy Pattern&#xff09;属于结构型设计模式&#xff0c;它为其他对象提供一种代理以控制对这个对象的访问。 通过引入代理对象&am…

【Steel Code】 10.5 COMPOSITE COLUMNS

10.5 COMPOSITE COLUMNS 组合柱 10.5.1 General 总则 (1) This clause applies for the design of composite columns and composite compression members with fully encased H sections, partially encased H sections, and infilled rectangular and circular hollow sect…

11.vector的介绍及模拟实现

1.vector的介绍 记得之前我们用C语言实现过顺序表&#xff0c;vector本质上也是顺序表&#xff0c;一个能够动态增长的数组。 vector 的底层实现机制 - 动态数组&#xff1a;vector 的底层实现是动态数组。它在内存中连续存储元素&#xff0c;就像一个可以自动调整大小的数…

封装(2)

大家好&#xff0c;今天我们来介绍一下包的概念&#xff0c;知道包的作用可以更好的面对今后的开发&#xff0c;那么我们就来看看包是什么东西吧。 6.3封装扩展之包 6.3.1包的概念 在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组…

go官方日志库带色彩格式化

go默认的 log 输出的日志样式比较难看&#xff0c;所以通过以下方式进行了美化和格式化&#xff0c;而且加入了 unicode 的ascii码&#xff0c;进行色彩渲染。 package mainimport ("fmt""log""os""runtime""strings""…

0基础学前端系列 -- 深入理解 HTML 布局

在现代网页设计中&#xff0c;布局是至关重要的一环。良好的布局不仅能提升用户体验&#xff0c;还能使内容更具可读性和美观性。HTML&#xff08;超文本标记语言&#xff09;结合 CSS&#xff08;层叠样式表&#xff09;为我们提供了多种布局方式。本文将详细介绍流式布局、Fl…

Windows开启IIS后依然出现http error 503.the service is unavailable

问题背景 已启用IIS服务&#xff0c;配置步骤可以参考Windows10 IIS Web服务器安装配置 问题描述 在这一步浏览网站时&#xff0c;并没有出现默认首页&#xff0c;而是 http error 503 the service is unavailable 问题解决 参考 成功解决http error 503.the service is un…

BuildCTF 公开赛web部分wp

文章目录 LovePopChainRedFlagWhy_so_serials?babyuploadeazyl0ginez!httpez_md5find-the-idsubtflock刮刮乐我写的网站被rce了&#xff1f; LovePopChain payload: <?php class MyObject{public $NoLove"Do_You_Want_Fl4g?";public $Forgzy;public functi…

diff 算法实现的几种方法和前端中的应用

diff 算法原理和几种实现方法 diff 是什么 diff 算法就是比较两个数据的差异&#xff0c;例如字符串的差异&#xff0c;对象的差异。 常用于版本管理&#xff08;git&#xff09;例如下面的实际案例。 github 上某个 commit&#xff0c;旧代码和新代码之间的不同 diff 展示…