【C语言】结构体与位段

2024-03-24 15:52
文章标签 语言 结构 位段

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

一、前言

  • 我们之前学习过定义一个整型类型的变量又或者定义一个浮点型类型的变量...,这些变量可以描述一个整数又或者描述一个小数...,可无论是整数还是小数...,它们也只是简单对象。
  • 如果我们想要描述像一本书,一个人这种复杂对象,用我们之前学习过的类型去描述是完全不够的。一本书包含着很多信息,如书名、价钱、作者、出版时期...。
  • 我们会发现书的其中一个信息的表示我们是学过的,我们也能将其描述出来,如书名啊,作者啊,可以用字符数组,价钱啊,可以用整型...。
  • C语言为我们提供了一种自定义类型结构体,并我们提供了一个声明结构体类型的关键字:struct。

二、如何正确使用结构体?

1.结构体的使用(声明+创建变量+初始化)
  • 小结:创建变量的同时,给成员 ’赋值‘ 的同时,才叫初始化,且可以对全部成员进行’赋值‘;而赋值不可以同时操作结构体的全部成员。
2.匿名结构体
  • 匿名结构体:省去结构体标签的结构体类型叫做匿名结构体
  • 小结:由上面两张代码截图可知,匿名结构体若不被关键字typedef重定义,则只能使用一次(在声明的同时,也进行变量的创建)。
3.结构体自引用
  • 在声明结构体类型时候,包含一个类型为自身的成员,可不可以?
  • 下面我将拿将对上面的People类型的结构体,增加一个成员,让它用来描述伴侣;
  • 第一个代码例子,类型于我们平常所说的无限套娃——结构体包含结构体,而这种做法,导致的是结构体的内存无限大,无法计算结构体的内存,所以这点,C语言是明令禁止的。
  • 第二个代码例子,我用一个结构体指针,若有伴侣了,我让它指向下一个结构体的地址,若无伴侣,则就填NULL;而指针类型的大小只与平台有关,64位平台8个字节,32位平台4个字节。
  • 所以第二代码例子,则是正确的自引用方式——包含自身结构体的指针。
4.结构体的两个操作符
  • 那我们又该如何逐一去访问该结构体的成员呢?其实C语言为我们提供了两个操作符(.)/(->);
  • 当我们知道了结构体的变量的时候,用操作符(.)
  • 当我们知道了指针结构体地址的指针变量的时候,用操作符(->)
  • 小结:(*xcf.girl).name   <----->  xcf.girl->name

三、结构体的内存对齐

1.什么是内存对齐?
  • 结构体占内存中占据的字节并非是简单的内存成员类型大小的标量和,而是存在内存对齐这一规则。
  • 1.结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处;
  • 2.其余的成员对齐到,(某个数字)对齐数,的整数倍处;
  • 对⻬数 = 编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值
  • VS默认对齐数为8;
  • Linux中gcc没有默认对齐数,对齐数就是成员自身的类型大小。
  • 3.若存在嵌套结构体的情况,则结构体成员对齐到,自己成员中最大对齐数的,整数倍处;
  • 4.结构体最终内存的大小,是结构体中最大对齐数的整数倍。
  • 结构体中每一个成员都有对应的对齐数,对齐数最大的则为最大对齐数。
2.为什么存在内存对齐?
  • 平台原因(移植原因):
  • 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  • 性能原因:
  • 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。
  • 小结:内存对齐是牺牲时间换空间的一种做法
3.修改默认对齐数

四、位段

1.位段的概念
  • 当我们学习完结构体后,就不得不了解下位段这一概念,何为位段?
  • 结构体的声明与位段的声明是相似的,换而言之,位段的实现是建立在结构体的概念上面的。
  • 位段与结构体有两点不同:
  • 位段的成员类型只能是整型;(如int,unsigned int,signed int,short,char等);
  • 位段的成员变量后面加上冒号与数字;(是位段与结构体最主要的区别),其中数字代表该变量在内存中所占据多少比特位。
  • 注:位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
  • 下面代码就是位段的声明举例,至于创建变量和初始化,和结构体一样
2.位段的内存分配
  • 位段一次性开辟内存的大小是按照所需要,开辟char、short、int大小的字节。
  • 位段最终的内存大小,是最大对齐数的整数倍。
  • 不同平台上,位段的内存分配不同,也就导致了相同位段,不同平台的内存大小不同;
3.位段的跨平台问题
  • 不同平台上,int是无符号整型,还是有符号整型是不确定的——VS上面int是由符号整型
  • 一次性开辟的内存空间是从左向右使用(从低地址->高地址),还是从右向左使用是不确定的——VS上面内存空间的使用是从右向左的;
  • 剩下的内存空间不够下一个位段成员的填充,是否浪费是不确定的——VS上面是选择浪费的
  • 下面是三个例子:(是我查找各种资源+咨询+寻找典型的例子,才得到的结果)
4.位段的注意事项
  • 由上面三组代码的三个内存布局的研究,我们可以知道,位段成员的起始位置不是某个字节的地址,而是莫个bite位的地址。
  • 我们在指针篇了解到地址是内存单元的编号,也就是一个字节的地址,而一个bite位是没有地址的。
  • 所以位段的成员是没有地址的,也就不能&操作,进行scanf输入。所以我们只能通过临时变量去赋值给位段成员。

这篇关于【C语言】结构体与位段的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

使用SQL语言查询多个Excel表格的操作方法

《使用SQL语言查询多个Excel表格的操作方法》本文介绍了如何使用SQL语言查询多个Excel表格,通过将所有Excel表格放入一个.xlsx文件中,并使用pandas和pandasql库进行读取和... 目录如何用SQL语言查询多个Excel表格如何使用sql查询excel内容1. 简介2. 实现思路3

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

结构体和联合体的区别及说明

《结构体和联合体的区别及说明》文章主要介绍了C语言中的结构体和联合体,结构体是一种自定义的复合数据类型,可以包含多个成员,每个成员可以是不同的数据类型,联合体是一种特殊的数据结构,可以在内存中共享同一... 目录结构体和联合体的区别1. 结构体(Struct)2. 联合体(Union)3. 联合体与结构体的

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

PostgreSQL如何查询表结构和索引信息

《PostgreSQL如何查询表结构和索引信息》文章介绍了在PostgreSQL中查询表结构和索引信息的几种方法,包括使用`d`元命令、系统数据字典查询以及使用可视化工具DBeaver... 目录前言使用\d元命令查看表字段信息和索引信息通过系统数据字典查询表结构通过系统数据字典查询索引信息查询所有的表名可

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl