7、用户自定义数据类型
1、结构体
1.1、结构体的定义
在实际的处理对象中,有许多信息是由多个不同类型的数据组合在一起进行描述,而且这些不同类型的数据是互相联系组成了一个有机的整体。此时,就要用到一种新的构造类型数据-----结构体,简称结构。
结构体的使用为处理复杂的数据结构(如动态数据结构等)提供了有效的手段,而且,它们为函数间传递不同类型的数据提供了方便。
结构体和数组一样,也是一种构造型数据类型,是用户自定义的新数据类型,在结构体中可以包含若干个不同数据类型和不同意义的数据项(当然也可以相同),从而使这些数据项组合起来反映某一个信息。
结构体是一种构造数据类型,结构体的语法如下:
Struct 结构体名
{
数据类型 成员名1;
数据类型 成员名2;
数据类型 成员名3;
….
数据类型 成员名n;
};
在花括号中的内容也称为“成员列表”或“域表”。其中每个成员的命名规则与变量名相同,成员之间用“;”作为结束符,整个结构的定义也用“;”作为结束符,数据类型可以是基本变量类型、数组类型、结构体类型、联合体类型或枚举类型等。
先定义结构体类型,再定义结构体变量。
结构体中的成员名可以与程序中的变量名相同,二者并不代表同一对象,编译程序可以自动对它们进行区分。
定义结构体类型时不会分配空间,只有用结构体类型定义变量时才会分配空间。
总结一下结构体类型的特点:
(1)、结构体类型是用户自行构造的
(2)、它由若干不同的基本数据类型的数据构成
(3)、它属于C语言的一种数据类型,与整形、浮点型相当。因此,定义它是不分配空间,只有用它定义变量时才分配空间。
1.2、结构体变量的声明、使用及初始化
1.2.1、结构体变量的声明
在定义了结构体类型后,就可以声明结构体类型的变量。有下面几种形式:
(1)、先定义结构体类型,再定义变量名
Struct 结构体名
{
类型 成员名;
类型 成员名;
};
Struct 结构体名 变量名1,变量名2…;
这里的结构体名是结构体的标识符,不是变量名。类型可以是基本的数据类型也可以是其它构造型数据类型。
Struct +结构体名:表示数据类型,两者不能分开写。
Struct 变量名1,变量名2…;这种写法是错误的,没有指明是哪种结构体类型。
结构体名 变量名1,变量名2…;这种写法也是错误的,因为没有struct关键字。
(2)、在定义数据类型的同时,定义变量
Struct 结构体名
{
类型 成员名;
类型 成员名;
}变量名1,变量名2…..;
(3)、直接定义结构体变量
Struct
{
类型 成员名;
类型 成员名;
}变量名1,变量名2…..;
1.2.2、结构体变量的使用
结构体变量是不同数据类型的若干数据的集合体。在程序中使用结构体变量时,一般情况下不能把它作为一个整体参加数据处理,而参加各种运算和操作的是结构体变量的各个成员项数据。
结构体变量的成员用以下一般形式表示:
结构体变量名.成员名;
结构体变量在使用时注意以下几点:
(1)、不能将一个结构体类型变量作为一个整体加以引用,而只能对结构体类型变量中的各个成员分别引用
(2)、如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级的找到最低的一级成员。只能对最低级的成员进行赋值或存取一级运算。
(3)、对成员变量可以像普通变量一样进行各种运算
(4)、在数组中,数组是不能彼此赋值的,而结构体类型变量可以相互赋值。在C语言中,同一结构体类型的结构体变量之间允许相互赋值,而不同结构体类型的结构体变量之间不允许相互赋值,即使两者包含有同样的成员。
1.2.3、结构体变量的初始化
与其他类型变量一样,也可以给结构体的每个成员赋初值,这称为结构体的初始化。一种是先定义结构体类型,再在定义结构体变量时进行初始化,语法格式如下:
Struct 结构体名 变量名 ={初始数据表};
另一种是在定义结构体类型时进行结构体变量的初始化。
Struct 结构体名
{
类型 成员名;
类型 成员名;
}变量名={初始数据表};
1.3、 结构体数组
1.3.1、结构体数组的定义
具有相同结构体类型的结构体变量也可以组成数组,称他们为结构体数组。结构体数组的每一个数组元素都是结构体类型的数据,他们都分别包括各个成员(分量)项。
定义结构体数组的方法和定义结构体变量的方法相仿,只需说明其为数组即可,可以采用以下方法:
(1)、先定义结构体类型,再用它定义结构体数组,定义形式如下:
Struct 结构体名
{
类型 成员名;
类型 成员名;
};
Struct 结构体名 数组名[元素个数];
(2)、定义结构体类型的同时,定义结构体数组,定义形式如下:
Struct 结构体名
{
类型 成员名;
类型 成员名;
}数组名[元素个数];
(3)、直接定义结构体数组,定义形式如下:
Struct
{
类型 成员名;
类型 成员名;
}数组名[元素个数];
1.3.2、结构体数组的初始化
结构体数组在定义的同时也可以进行初始化,并且与结构体变量的初始化规定相同。
结构体数组初始化的一般形式时:
Struct 结构体名 { 类型 成员名; 类型 成员名; }; Struct 结构体名 数组名[元素个数]={初始数据表}; 或者 Struct 结构体名 { 类型 成员名; 类型 成员名; }数组名[元素个数] ={初始数据表}; 或者 Struct { 类型 成员名; 类型 成员名; }数组名[元素个数] ={初始数据表};
由于结构体变量是由若干不同类型的数据组成,而结构体数组又是由若干结构体变量组成。所以要特别注意包围在大括号中的初始数据的顺序,以及它们各个成员项间的对应关系。
1.3.3、结构体数组的使用
一个结构体数组的元素相当于一个结构体变量,因此前面介绍的有关结构体变量的规则也适用于结构体数组元素。
1.4、结构体指针
可以设定一个指针变量用来指向一个结构体变量。此时该指针变量的值是结构体变量的起始地址,该指针称为结构体指针。
结构体指针与前面介绍的各种指针变量在特性和方法上是相同的。与前述相同,在程序中结构体指针也是通过访问目标运算“*”访问它的对象。结构体的指针在程序中的一般定义形式为:
Struct 结构体名 *结构指针名;
其中的结构体名必须是已经定义过的结构体类型。
结构体指针访问结构体成员:结构体指针->成员名;
结构体变量名代表的是整个集合本身,作为函数参数时传递的整个集合,也就是所有成员,而不是像数组一样被编译器转换成一个指针。
1.5、结构体嵌套结构体
结构体中的成员也可以是另外一个结构体。
1.6、结构体做函数参数
将结构体作为参数向函数中传递,传递方式有两种:值传递和地址传递。
struct student { }; void temp(struct student S);//值传递 void temp(struct student *S);//地址传递
1.7、结构体中const使用场景
结构体中const用来防止误操作。
void temp(const struct student *S);//地址传递,加const后,只能读结构体中的数据,不能修改结构体中的数据
2、位域
2.1、位域的定义
计算机的内存是以字节为单位,为变量分配内存,也是以字节为单位,但是,实际上,有些信息的存储,并不需要占用一个字节,只需要一个或几个二进制位就够了。
所谓位域就是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
位域的定义与结构体的定义很相似,其一般形式如下:
Struct 位域结构名
{
位域列表;
};
其中位域列表的形式为:数据类型 位域名:位域长度;
例如: Struct data { Unsigned char a:2; //1~2位 Unsigned char b:3; //3~5位 Unsigned char c:3; //6~8位 }
关于位域的定义,有一些问题需要注意:
(1)、各位域必须存储在同一字节里,不能跨两个字节
(2)、位域占用的位数,不能超过8个二进制位
(3)、允许位域无域名
Struct data { Unsigned int a:2; //1~2位 Unsigned int b:5; //3~7位 Unsigned int c:3; //8~10位 该位跨字节了 };
当一个字节所剩空间不够存放另一位域时,应从下一字节单元起开始存放该位域,也可以有意使某位域从下一单元开始。可以使用下面的方式,来解决这个问题:
Struct data { Unsigned int a:2; //1~2位 Unsigned int b:2; //3~4位 Unsigned int :0; //5~8位 空域 填0表示该字节剩余的位不用 Unsigned int c:5; // 1~5位 Unsigned int d:1; // 6位 Unsigned int e:2; // 7~8位 Unsigned int f:3; // 1~3位 Unsigned int :2; // 4~5位 无名位域,它只是用来填充或调整位置,无名的位域是不能使用的 Unsigned int g:3; // 6~8位 };
2.2、位域变量的说明
(1)、先定义位域类型,在声明变量
例如: Struct data { Unsigned int a:2; //1~2位 Unsigned int b:3; //3~5位 Unsigned int c:3; //6~8位 }; Struct data t1,t2;
(2)、定义位域类型的同时,声明变量
例如: Struct data { Unsigned int a:2; //1~2位 Unsigned int b:3; //3~5位 Unsigned int c:3; //6~8位 }t1,t2; 和结构体类似,这种方式声明的变量,多是全局变量
(3)、直接定义位域类型的变量
Struct { Unsigned int a:2; //1~2位 Unsigned int b:3; //3~5位 Unsigned int c:3; //6~8位 }t1,t2;
这种方式,由于位域没有类型名,因此,不能再程序的其它位置,继续声明变量。这种写法,通常在函数内部。
2.3、位域的使用
位域的使用和结构体成员的使用相同,其一般形式为:
位域变量名.位域成员名
3、共用体
在C语言中,不同数据类型的数据可以使用共同的存储区域,这种数据构造类型称为共用体,简称公用,又称联合体。共用体在定义、说明和使用形式上和结构体相似。两者本质上的不同仅在于使用内存的方式上。
定义一个共用体类型的一般形式为:
Union 共用体名
{
数据类型 成员;
数据类型 成员;
数据类型 成员;
……
};
共用体中的所有成员使用同一块内存,所有成员的起始地址是一样的,内存的大小由数据类型最大的成员决定。
由于共用体中各成员的数据长度往往不同,所以共用体变量在存储时总是按其成员中数据长度最大的成员占用内存空间。在这一点上,共用体和结构体不同,结构体类型变量在存储时总是按各成员的数据长度之和占用内存空间。因此,当多个数据需要共享内存或者多个数据每次只取其一时,可以用共用体。
数据的低位存放在低地址上。
共用体也可以和位域结合起来使用。
4、枚举
在C语言中还有一种构造类型,即枚举。在实际问题中,有些变量只有几种可能的取值。例如:一周只有七天,程序中的错误只有那么几种等。针对这样特殊的变量,C语言提供了“枚举”类型,在枚举的定义中,会将变量的值一一列出来。当然,枚举类型的变量的值也就只限于列举出来的值得范围内。
4.1、枚举类型的定义
枚举类型的定义形式如下:
enum 枚举名
{
枚举成员列表;
};
在枚举成员列表中列出所有可能的取值,以分号结尾。注意和结构体、联合体类似,“enum 枚举名”是新定义的类型名。
枚举类型变量只占4个字节。
枚举值默认从0开始,往后逐个加1,如果第一个成员不赋值,就默认为0;如果第一个成员赋值,那么后面的成员就从第一个成员的值逐渐递增。
枚举成员不能具有相同的名称,枚举成员都是常量。

浙公网安备 33010602011771号