理解C语言——从小菜到大神的晋级之路(10)——结构体、联合体

2023-11-23 04:38

本文主要是介绍理解C语言——从小菜到大神的晋级之路(10)——结构体、联合体,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本节视频链接:点击这里


        上篇中讲述的数组是复合数据类型中最简单的一种,一个数组使用一段连续的内存保存了若干个类型相同的数据元素。由于类型和长度相同,数组的每个元素通过数组下标和指针变量访问。如果我们希望一个结构保存多个不同类型的数据元素,那么数组将无能为力。为了实现这样的功能,C语言提供了结构体和联合体。



1、结构体基本概念


(1)结构体的定义


        假设我们需要定义一个图形中的点的概念。在一个使用笛卡尔坐标系表示图像的系统中,点的位置使用两个坐标分量表示,即横坐标x和纵坐标y。那么为了定义点这个变量,我们需要将坐标的两个分量定义于一起。将坐标x和坐标y定义在结构体中的方法为:
struct Point
{int x;int y;
};

        定义一个结构体使用关键字struct实现。关键字struct定义了结构体名称,并在其后用大括号{ }指明了结构体的成员。每一个结构体成员是该结构体实例中所包含的数据。如我们定义的Point结构中就包含了x和y两个int型数据成员。在定义结构体的同时可以定义结构体的实例,如:

struct Point
{int x;int y;
} pt1, pt2, *ppt1;

        另一种方法也可以将定义结构体类型和定义结构体实例分开:

struct Point
{int x;int y;
};
struct Point pt1, pt2, *ppt1;

        在定义结构体时,更常用是使用typedef定义一种新的类型,这样后面再定义新的结构体实例时,便不需要再添加关键字struct,使得我们定义的结构体更像C语言提供的其他基本数据类型一样,使用更加简洁。

typedef struct _Point
{int x;int y;
} Point;
Point pt1, pt2, *ppt1;


(2)结构体成员的访问


        当我们定义了结构体实例后,可以轻松访问包含在结构体中的各种变量。例如我们定义了以下的Point变量:
Point pt1 = {1,3}, pt2 = {4, 8}, *ppt1 = &pt1;

        使用结构体成员运算符“.”,可以按照结构体成员的名称获取其数值:

printf(“Point 1: (%d, %d), Point 2: (%d, %d)\n”, pt1.x, pt1.y, pt2.x, pt2.y);
float distance = sqrt(pow(pt1.x-pt2.x, 2) + pow(pt1.y-pt2.y, 2));
printf(“Distance of two points: %f”, distance);

        对于结构体实例,可以直接访问使用其内部成员,而使用指向结构体实例的指针也可以间接访问结构体的成员,其方法不再是使用”.”而是”->”。例如:

printf(“Point 1: (%d, %d)\n”, ppt1->x, pt1->y);

        对于使用指针访问结构体成员,其效果同使用实例访问结构体成员是一致的。在实际使用时,使用指针的情况还更加频繁。


(3)结构体成员的初始化


        从前面的程序中可以看出,结构体的初始化方法同数组的初始化方法比较类似,使用的是一对大括号所包括的、由逗号分隔的多个初始值。同初始化数组不同的是,由于结构体中的成员可以是不同的数据类型,初始化结构体的数据也可以依据结构体成员的定义类型不同而不同。结构体成员类型相同时的初始化方法已在上一节有所体现,现在我们来定义一个更复杂的结构体:
typedef struct _Student
{long long student_id;char *student_name;char student_gender;int student_age;float student_height;float student_weight;
} Student;

        定义两个结构体实例,并将其初始化:

Student Jack = {201601001, “Jack”, ‘F',17, 180.5, 190.0}, Alice = {201601002, “Alice”, ‘M’, 16, 165.0, 140.8};

        在这两个实例进行初始化时,我们根据定义的数据不同,分别包含了整型、字符串、字符型和浮点型等数据类型,并在初始化时按照不同类型对其成员变量进行了赋初值。如果我们只初始化了前面一部分的成员,那么剩余的结构体成员将被初始化为缺省值。


2、结构体的存储结构



        当我们定义了一个结构体时,有时需要对其存储结构有一定了解。通常情况下,一个结构体的实例的大小等同于结构体内部各个成员大小的总和,因为程序编译时编译器会按照成员列表的顺序一个一个给每一个成员分配内存,但是这只有在满足内存边界对其时才是这种情况。如果定义的结构体成员之间大小关系不满足边界对其要求,那么成员之间可能会出现用于填充的额外内存空间。

        例如我们定义一个结构体:
typedef struct  
{char   ChrVal1;int      IntVal;char   ChrVal2;
} MemAlign;
MemAlign ma1 = {10, ‘A’, ‘b'};

        结构体实例ma1在内存中的实际大小因机器不同而异。如果某个机器的的整型值长度诶4 Byte,且起始存储位置必须能被4整除,则这一结构在内存中的大小为12,而不是1+4+1=6。


        结构体成员的存储必须满足几个原则:
  1. 某一个结构体对象的起始存储位置必须是最大的成员大小的整数倍;
  2. 每个结构体成员相对于本结构体起始位置的偏移量必须是自身大小的整数倍;
  3. 结构体对象所占据的总大小必须是最大数据成员体积的整数倍。如果以上三个条件有任何一个不满足,都将会在相应位置补充填充字节使之满足条件。

        鉴于此,如果不涉及到程序可读性和可维护性的前提下,我们可以重新排列结构体成员的顺序来提高内存的利用效率。如MemAlign结构按照如下方式声明,将只占据8个字节,相对于上文的声明方法存储效率提高了1/3。
typedef struct  
{int      IntVal;char   ChrVal1;char   ChrVal2;
} MemAlign;



3、位段



        位段是结构体中一种特殊的成员变量类型。位段的声明方式同普通的结构体成员类似,但是位段所代表的是一个或多个位(bit)的数据。所有的位段成员必须声明为int/signed/unsigned int类型。合适地使用位段可以根据需要更高效地利用内存,但是可能会降低程序的可移植性。这主要是由于以下情况根据系统的不同而不同:
  • int位段被作为有符号还是无符号;
  • 一个位段中允许的最大bit数;高位系统上允许的程序在低位系统上可能无法运行;
  • 位段成员在内存中分配的顺序(从左到右/从右到左);
  • 体积较大的后续位段的存放位置(直接紧邻前一个/从下一个字开始);
        例如,一个使用位段的结构体声明如下:
typedef struct {unsigned ch: 7;unsigned font: 6;unsigned size: 19;
} CHAR;
CHAR ch1;


4、联合体


(1)联合体的概念和使用


        联合体是C语言中提供的另一种结构。联合体的声明方式同结构体类似,但是它的结构和行为方式同结构体完全不同。结构体中使用不同的内存位置包含了不同的成员,而联合体则是使用相同的内存位置代表不同的联合体成员,而其中内存实际的数据都是相同的。例如,定义一个简单的联合体:
union {float f;int    i;
} fi;

        该联合体实例只占用1个32位的内存空间,其中的f和i成员指代的都是同一段内存。如果我们对其中的一个成员赋值,那么联合体其他成员所指代的含义通常是用其他的格式表示内存中相同的位:

fi.f = 3.14159;
int x = fi.i;

        如果一个联合体中各成员的长度不同,那么联合体的长度是其最长的成员的长度。

(2)联合体实例如何初始化:


        初始化一个联合体实例必须采用联合体第一个成员的类型,而且该值必须位于大括号内部,例如:
union {float f;int    i;
} fi = {3.14};

这篇关于理解C语言——从小菜到大神的晋级之路(10)——结构体、联合体的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

使用Java实现通用树形结构构建工具类

《使用Java实现通用树形结构构建工具类》这篇文章主要为大家详细介绍了如何使用Java实现通用树形结构构建工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录完整代码一、设计思想与核心功能二、核心实现原理1. 数据结构准备阶段2. 循环依赖检测算法3. 树形结构构建4. 搜索子

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

C语言实现两个变量值交换的三种方式

《C语言实现两个变量值交换的三种方式》两个变量值的交换是编程中最常见的问题之一,以下将介绍三种变量的交换方式,其中第一种方式是最常用也是最实用的,后两种方式一般只在特殊限制下使用,需要的朋友可以参考下... 目录1.使用临时变量(推荐)2.相加和相减的方式(值较大时可能丢失数据)3.按位异或运算1.使用临时

使用C语言实现交换整数的奇数位和偶数位

《使用C语言实现交换整数的奇数位和偶数位》在C语言中,要交换一个整数的二进制位中的奇数位和偶数位,重点需要理解位操作,当我们谈论二进制位的奇数位和偶数位时,我们是指从右到左数的位置,本文给大家介绍了使... 目录一、问题描述二、解决思路三、函数实现四、宏实现五、总结一、问题描述使用C语言代码实现:将一个整

C语言字符函数和字符串函数示例详解

《C语言字符函数和字符串函数示例详解》本文详细介绍了C语言中字符分类函数、字符转换函数及字符串操作函数的使用方法,并通过示例代码展示了如何实现这些功能,通过这些内容,读者可以深入理解并掌握C语言中的字... 目录一、字符分类函数二、字符转换函数三、strlen的使用和模拟实现3.1strlen函数3.2st

Go语言中最便捷的http请求包resty的使用详解

《Go语言中最便捷的http请求包resty的使用详解》go语言虽然自身就有net/http包,但是说实话用起来没那么好用,resty包是go语言中一个非常受欢迎的http请求处理包,下面我们一起来学... 目录安装一、一个简单的get二、带查询参数三、设置请求头、body四、设置表单数据五、处理响应六、超

C语言中的浮点数存储详解

《C语言中的浮点数存储详解》:本文主要介绍C语言中的浮点数存储详解,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、首先明确一个概念2、接下来,讲解C语言中浮点型数存储的规则2.1、可以将上述公式分为两部分来看2.2、问:十进制小数0.5该如何存储?2.3 浮点

基于Python实现多语言朗读与单词选择测验

《基于Python实现多语言朗读与单词选择测验》在数字化教育日益普及的今天,开发一款能够支持多语言朗读和单词选择测验的程序,对于语言学习者来说无疑是一个巨大的福音,下面我们就来用Python实现一个这... 目录一、项目概述二、环境准备三、实现朗读功能四、实现单词选择测验五、创建图形用户界面六、运行程序七、