码迷,mamicode.com
首页 > 其他好文 > 详细

自定义类型的学习总结

时间:2018-12-03 00:44:37      阅读:254      评论:0      收藏:0      [点我收藏+]

标签:inf   等等   直接   简单的   读取   zha   它的   hide   例子   

在C语言中,自定义类型是一种应用非常广泛的,典型的以结构体为例,比如你要描述一个学生,这个学生具有:姓名+年龄+性别+学号 这么几项特征,而通常我们有可能要把学生来包装成一个类型,这样就可以重复性的用这个类型来定义出不同的每个学生。而结构体的出现就可以使得C语言具有了这样能描述复杂类型的能力。

一般的自定义类型我们主要学习这么几种:结构体,枚举,联合体

知识点: 
>结构体类型创建 
>结构体初始化 
>结构体内存对齐 
>位段,位段计算机大小。 
>枚举+联合。 

首先是看看结构体,关于结构体,其实我们可以联想到数组,因为结构体于数组是有一定程度的相似之处的,都是属于聚合性结构,可以用来存放多个元素,不同点在于数组只能存放不同类型的元素,而结构体则可以存放相同或不同类型的元素。

结构体类型创建

首先要声明一个结构体类型,我们需要用到关键字struct ,这里给出声明一个结构体的一般格式:

技术分享图片
1 struct tag
2 {
3      member-list;
4 }variable-list;
View Code

在这里要说明的是,这个tag被称为结构体的类型,也就是我们自己定义的这个结构体的名字,这个是可以省略的,当它被省略时,我们定义的结构体就被叫做匿名结构体。

而variable-list,也就是结构体的变量,这是代表我们实际上定义的结构体类型的这样的变量,可以在这里直接同时声明出多个同类型的变量。当然,也允许省略,省略的代价无非是我们只是在抽象出了一个结构体类型而已,等到在程序中要实际定义出这个结构体变量时,直接用这个类型来定义就可以。结构体的变量,可以被定义成一个指针,那么这个指针,也就是结构体类型的指针了。

但是,这里要强调的是,这个member-list,也就是结构体的内部成员,就是比如当我们定义一个学生时,用来描述这个学生的,姓名,学号,性别等这些属性信息。那他可以省略吗。那么我们要说的是,当我们省略结构体成员时,这就是一个空结构体了。注意:在C语言中,空结构体的情况是不被允许的,你必须至少包含一个成员。(PS:在C++里,其实结构体就是类这么一个概念,而类是允许有空类存在的)

这里的三点:结构体的类型,结构体变量以及结构体的内部成员,我们统称为结构体的三要素。这三要素,是组成一个结构体类型的重要组成部分。

结构体初始化

结构体的初始化规则,与数组是一样的,当我们需要对结构体变量初始化,也就是定义它的同时直接赋给它初值,是可以进行整体赋值的,将一组要赋的初值用{ }包起来,整体赋值就行了。比如:

技术分享图片
1 struct Point 
2 {
3     int x;
4     int y;
5 }p1;   //声明类型的同时定义变量p1
6 struct Point p2; //定义结构体变量p2
7 
8 struct Point p3={x,y};  //初始化:就是定义变量的同时赋初值
View Code

再比如定义一个学生:

技术分享图片
1 struct student
2 {
3     char name[10];
4     int age;
5 };
6 struct student s={"zhangsan",20};//直接将学生初始化成某个具体的学生
View Code

结构体体的内存对齐

谈到结构体的内存对齐,结构体的内存对齐是作为计算结构体大小的时候,需要用到的一个重要概念,总体来说,属于一种依靠浪费空间来换性能的方法。这里我先给出一个例子,来简单说明。我们都知道,cpu访问内存时,我们以为它是可以在内存任意位置读取的,但实际上,某些平台可能只能在特定位置访问。比如,cpu读取内存数据,假设起始地址必须从偏移量为4的整数部分读取。那么,现在定义这么一个结构体,我们分别画图分析内存对齐和不进行对齐时,它是如何访问内存的。

技术分享图片
1 struct {
2     char x;
3     int y;
4 };
View Code

技术分享图片

可以看见,当我们在内存中读取结构体当中的成员时,由于char 型的A占一个字节,而int型的B占四个字节,

那么假设他们都从偏移量为4的这个位置开始,只访问4的整数倍数。可以看出,不内存对齐时,访问A只要一步,读够一个字节就好,访问B时,接着A的位置继续,就要先读完A剩下的三个字节,再从8的位置起,再读一个字节,补够B所需四个字节。这样读取B就需要两次。而当我们进行内存对齐,也就是假设我A后面三个字节不要了,我直接从8起,把B放在这四个字节里。这时我读取A,只需要一步,再从8开始读取B,也只需一步就能读够所要的四个字节。速度上我就可以提高,这在某些需要性能的程序上是很重要的。

所以总的来说,内存对齐,就是一种用空间换时间的做法。至于深入的探究,这里暂时先不讨论。

位段,位段计算机大小

位段是一种与结构体很类似的自定义类型,在声明上,主要有两个特点:

  1. 位段成员必须是int ,unsigned int或signed int也或者char (即整型家族);
  2. 位段成员名后面有一个冒号和一个数字(ps:数字就是用于表示该成员占据的字节数)。
技术分享图片
1 struct A
2 {
3     int _a:2;
4     int _b:5;
5     int _c:10;
6     int _d:10;
7 };
View Code

 那么我们计算位段一个位段类型变量时,其实就是将这些数字加在一块就可以了,而位段其实就是通过比特位压缩存储的方式来存储的。

当我们存储一个变量时,计算机会先看这个变量前面的一个变量是否将为之开辟的空间用完,如果发现前一个变量实际上没有用完空间。再存储这个变量,是接着前一个变量继续存储的,而不是继续开辟新的空间。可以看出,位段是这样子,与前面结构体内存对齐的概念相反,没有对内存的浪费,而是存储的非常紧促,是以牺牲性能的方法换取内存上的节省。

位段有一个很重要的限制在于,它是不支持跨平台的,所以如果是在考虑跨平台移植的程序里,就无法使用位段了。

枚举+联合

 最后简单说说枚举和联合这两个概念,所谓枚举,可以理解为一一列举。

枚举在自定义类型里,有一个很与众不同的特性,就是它的内部所有成员,都不是变量,而是常量,并且成员之间是以逗号间隔的。也就是说,所谓枚举,只是简单的将其包含的成员一一列举出来,而不作任何改变。举个例子:一个星期从周一周日,这是可以列举出来,而无法进行改变的;再比如颜色有好多种可一一列举,一年十二个月等等,这些都是可以的。枚举是以关键字enum定义的。

技术分享图片
 1 enum Day
 2 {
 3     Mon,
 4     Tues,
 5     Wed,
 6     Thur,
 7     Fri,
 8     Sat,
 9     Sun
10 };
View Code

使用枚举是可以用来定义变量的,既然是定义变量,那可以与另一个定义变量的工具--宏进行对比,我们可以发现,枚举,可以一次性定义多个常量,而宏只能一个一个定义了。从效率上就可以分出胜负了,并且枚举作为一种类型。是有着严格的类型检查的,所以相对来说,会更加严谨一点。

最后再看看联合体,联合体我们也叫作共用体,是以关键字union来声明的。一个很重要的特征是,联合体当中所有的成员是公用的同一块空间,可以理解为其他自定义类型中,成员是一个跟在一个后面的排布,而联合体中成员是横排排列的。这样,所有的成员就都是第一个元素,联合体的地址,是所有成员的地址。每个成员的首地址,都是相同的。而整个联合体的大小,则只需要计算所有成员里面占据空间最大的那个就可以了。

技术分享图片
1 union Un
2 {
3     int i;
4     char c;         
5 
6 };
View Code

 

自定义类型的学习总结

标签:inf   等等   直接   简单的   读取   zha   它的   hide   例子   

原文地址:https://www.cnblogs.com/sunjiyuan/p/10051991.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!