我们已经知道C语言中有整型、浮点型和字符型三种基本数据类型,同一种类型的数据的集合是数组,多种类型的数据的集合就是结构体。
结构体是类似于名片形式的数据集合体,可以把它理解为一种由用户自定义的特殊的复合型的“数据类型”,在这个复合型的“数据类型”中可以包含多种基本数据类型,我们可以把它作为一个整体来操作。
就像是某个公司做好一个名片模板为其员工制作统一样式的名片,上面可以印上公司名称、姓名、职务、联系电话、E-mail、地址等(见图 1),结构体就类似于这个制作名片的空白模板。
如同在制作统一样式的员工名片之前,先要设计一个名片模板一样,在 C语言中使用结构体数据之前,先要对结构体进行声明,结构体的声明如同制作一个包含多种数据的空白卡片。一个包含多种数据的结构体的声明格式如图 1(b)所示。
其中,student 是结构名(structure tag);{ } 中的 name、sex、height、weight 称为结构体成员,每个结构体成员都表示一个 C语言基本数据类型的数据,这些基本类型的数据集中起来组成了一个复合型的新数据类型 struct student 型。
也就是说,结构体的声明只是某种新的数据类型的声明,这个声明并没有定义具体的对象(变量)的实体。两个单词的组合 struct student 就是这个新的数据类型的类型名,它的使用方式如同表示整型的类型名 int 一样。
图 2b) 中的代码所声明的这个“结构体”,相当于仅仅制作了一个类似图 2a) 中的“学生基本信息登记卡”(空白卡片)。
结构体的声明只是定义了一种数据类型,并没有定义对象实体,内存中也没有生成任何变量。只有定义了结构体对象,才会在内存中生成一个由结构体成员变量组成的变量集合体。结构体对象的定义及初始化如同把卡片分配给个人。结构体对象的定义格式如下:
struct 结构名 对象名; //“struct 结构名”就是结构体的数据类型名
结构体对象的定义和 C语言基本数据类型变量(如 int 型变量)的定义是一样的(见图 3),即“类型名+变量名”的形式。结构体这种自定义的数据类型的类型名比较特殊,是“struct 结构名”。
上面讲到结构体 student 的声明如同制作了空白的学生信息登记卡,那么结构体对象的声明就好像是把这些空白的登记卡分配给具体的同学(见图 4)。定义一个结构体对象以后,内存中就会生成一个对象的实体,这个对象实体是由结构体成员变量组成的变量集合体,并且由对象名来标识这个变量集合体。
结构体对象可以在定义的同时对成员进行初始化赋值,方法如同数组的初始化,即把各个结构体成员的初始值依次排列在{ }中,并用逗号分隔。如:
struct student tony = {"Tony","男",160,45.8};
结构体声明、定义结构体对象并初始化可以由一条 C 语句完成,如下面代码在声明结构体 student 的同时定义了一个结构体 student 的对象 tony 并同时将其成员变量初始化赋值。
struct student{ //声明结构体:student
char name[64];
char sex;
int height;
float weight;
} tony = {"Tony","男",160,45.8}; //定义结构体对象tony并初始化
结构体对象 tony 是由结构体 student 的成员变量组成的变量集合体,{ } 中的值依次赋值给这些成员变量 name、sex、height、weight(见图 5)。
结构体对象是由结构体成员变量组成的变量集合体(一个整体),并且像数组一样存储在连续的内存空间中,因而我们可以单独对组成对象的结构体成员变量进行访问。
访问结构体对象成员时使用运算符“.”,该运算符称为句点运算符,具体形式是“对象名.成员名”。例如图 6 中访问结构体对象 tony 的成员 name 的表达式如下所示:
tony.name //对象名.成员名
tony.name 是一个 char 型的数组变量,所以对它的操作和普通的数组变量一样,比如用字符串复制函数 strcpy(tony.name,"Kitty") 修改它的值。
tony.height 则可以访问 tony 的成员 height,它是一个 int 型变量,它和普通的 int 型变量一样可以直接进行赋值和取值操作。
结构体和数组在处理多个对象的集合方面具有诸多相同点,因而它们在 C 语言中被统称为聚合类型。
但两者也有明显的不同点,数组被用于高效处理“相同类型”的数据的集合,而结构体通常被用于高效处理“不同类型”的数据的集合(偶尔也会有成员类型全部相同的情况)。
此外,在可否整体赋值方面也不同。即便两个数组的类型和元素个数相同,它们也不能用赋值符=相互赋值(数组的赋值通常用 strcpy( ) 函数)。但是,具有相同类型的两个结构体对象却可以用=相互赋值(见图 7)。
int a[5],b[5];
a = b; //错误的数组赋值方法
struct student peter,tony = {"Tony","男",160,45.8};
peter = tony; //OK