C语言自定义数据类型(一)定义和使用结构体变量

C 语言提供了一些由系统已定义好的数据类型,如:int,float,char  等,用户可以在程序中用它们定义变量,解决一般的问题。

但是人们要处理的问题往往比较复杂,只有系统提供的类型还不能满足应用的要求,C语言允许用户根据需要自己建立一些数据类型,并用它来定义变量。

目录


一、自己建立结构体类型

1.1引入

在前面所见到的程序中,所用的变量大多数是互相独立、无内在联系的。例如定义了整型变量a,b,c,它们都是单独存在的变量,在内存中的地址也是互不相干的,但在实际生活和工作中,有些数据是有内在联系的,成组出现的。例如,一个学生的学号、姓名、性别、年龄、成绩、家庭地址等项,是属于同一个学生的。可以看到性别( sex )、年龄( age )、成绩( score )、地址( addr )是属于学号为 1001 和名为 Li Ming 的学生的。如果将num,name,sex,age,score和 addr 分别定义为互相独立的简单变量,难以反映它们之间的内在联系。人们希望把这些数据组成一个组合数据,例如定义一个名为 student_1 的变量,在这个变量中包括学生 1 的学号、姓名、性别、年龄、成绩、家庭地址等项。这样,使用起来就方便多了。

能否用一个数组来存放这些数据呢?显然不行,因为一个数组中只能存放同一类型的数据。例如整型数组可以存放学号或成绩,但不能存放姓名、性别、地址等字符型的数据。C 语言允许用户自己建立由不同类型数据组成的组合型的数据结构,它称为结构体 ( structure )。

如果程序中要用到图中表示的数据结构,可以在程序中自己建立一个结构体类型。

例如:

struct Student
{
	int num;		    //学号为整型
	char name[20];		//姓名为字符串
	char sex;			//性别为字符型
	int age;			//年龄为整型
	float score;		//成绩为实型
	char addr[30];		//地址为字符串
};

1.2定义

上面由程序设计者指定了一个结构体类型 struct Student ( struct 是声明结构体类型时必须使用的关键字,不能省略),经过上面的指定, struct Student 就是一个在本程序中可以使用的合法类型名,它向编译系统声明:这是一个 “ 结构体类型 ”,它包括 num,name,sex,age,score,addr 等不同类型的成员。它和系统提供的标准类型(如 int,char,float,double 等)具有相似的作用,都可以用来定义变量,只不过 int 等类型是系统已声明的,而结构体类型是由用户根据需要在程序中指定的。

声明一个结构体类型的一般形式为:

struct 结构体名
{ 成员表列 };

注意:

结构体类型的名字是由一个关键字 struct 和结构体名组合而成的(例如 struct Student )。结构体名是由用户指定的,又称 “ 结构体标记 ”( structure tag ),以区别于其他结构体类型。上面的结构体声明中 Student 就是结构体名(结构体标记)。

花括号内是该结构体所包括的子项,称为结构体的成员(member)。上例中的 num,name,sex 等都是成员。对各成员都应进行类型声明,即

类型名成员名;

“ 成员表列 ”( member list )也称为 “ 域表 ” ( field list),每一个成员是结构体中的一个域。成员名命名规则与变量名相同。

1.3拓展

(1)结构体类型并非只有一种,而是可以设计出许多种结构体类型,例如除了可以建立上面的struct Student 结构体类型外,还可以根据需要建立名为 struct Teacher,struct Worker 和 struct Date 等结构体类型,各自包含不同的成员。

(2)成员可以属于另一个结构体类型。例如:

struct Date
{
	int year;		//年
	int month;		//月
	int day;		//日
};
struct Student
{
	int num;
	char name[20];
	char sex;
	int age;
	struct Date birthday;	//成员birthday属于struct Date类型
	char addr[30];
};

先声明一个 struct Date 类型,它代表 “日期 ”,包括 3 个成员:year(年),month(月) ,day(日)。然后在声明 struct Student 类型时,将成员 birthday 指定为 struct Date 类型。已声明的类型 struct Date 与其他类型(如 int,char )一样可以用来声明成员的类型。

二、定义结构体类型变量

前面只是建立了一个结构体类型,它相当于一个模型,并没有定义变量,其中并无具体数据,系统对之也不分配存储单元。相当于设计好了图纸,但并未建成具体的房屋。为了能在程序中使用结构体类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据。可以采取以下 3 种方法定义结构体类型变量。

2.1先声明结构体类型,再定义该类型的变量

在 1.1 节的开头已声明了一个结构体类型 struct Student,可以用它来定义变量。例如:

struct Student student1, student2;

这种形式和定义其他类型的变量形式 ( 如 int a,b;)是相似的。上面定义了 student1 和 student2 为 struct Student 类型的变量,这样 student1 和 student2 就具有 struct Student 类型的结构。

在定义了结构体变量后,系统会为之分配内存单元。根据结构体类型中包含的成员情况,在 Visual C++ 中占 63 个字节(4+20+1+4+4+30 = 63)。

这种方式是声明类型和定义变量分离,在声明类型后可以随时定义变量,比较灵活。

2.2在声明类型的同时定义变量

例如:

struct Student
{
	int num;
	char name[20];
	char sex;
	int age;
	float score;
	char addr[30];
} studentl, student2;

它的作用与第一种方法相同,但是在定义 struct Student 类型的同时定义两个 struct Student 类型的变量 student1 和 student2。这种定义方法的一般形式为

struct 结构体名

{

        成员表列

} 变量名表列;

声明类型和定义变量放在一起进行,能直接看到结构体的结构,比较直观,在写小程序时用此方式比较方便,但写大程序时,往往要求对类型的声明和对变量的定义分别放在不同的地方,以使程序结构清晰,便于维护,所以一般不多用这种方式。

2.3不指定类型名而直接定义结构体类型变量

其一般形式为:

struct
{

        成员表列

}变量名表列;

指定了一个无名的结构体类型,它没有名字( 不出现结构体名 )。显然不能再以此结构体类型去定义其他变量。这种方式用得不多。

2.4拓展

(1)结构体类型与结构体变量是不同的概念,不要混淆。只能对变量赋值、存取或运算,而不能对一个类型赋值,存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间。

(2)结构体类型中的成员名可以与程序中的变量名相同,但二者不代表同一对象。例如,程序中可以另定义一个变量 num,它与 struct Student 中的 num 是两回事,互不干扰。

(3)对结构体变量中的成员(即 “域” ),可以单独使用,它的作用与地位相当于普通变量。关于对成员的引用方法见下节。

三、结构体变量的初始化和引用

在定义结构体变量时,可以对它初始化,即赋予初始值。然后可以引用这个变量,例如输出它的成员的值。

3.1举例说明

举例:把一个学生的信息 (包括学号、姓名、性别、住址) 放在一个结构体变量中,然后输出这个学生的信息。

解题思路:先在程序中自己建立一个结构体类型,包括有关学生信息的各成员。然后用它来定义结构体变量,同时赋予初值 ( 学生的信息 )。最后输出该结构体变量的各成员 ( 即该学生的信息 )。

#include<stdio.h>
struct Student			//声明结构体类型
{
	long int num;
	char name[20];
	char sex;
	char addr[30];
}student1 = { 1001,"Zhang San",'M',"Beijing Road" };	//定义结构体变量student1,并初始化

int main()
{
	printf("NO.:%ld\nname:%s\nsex:%c\naddress:%s\n", student1.num, student1.name, student1.sex, student1.addr);
}

运行结果:

程序分析:

程序中声明了一个结构体名为 Student 的结构体类型,有 4 个成员。在声明类型的同时定义了结构体变量 student1,这个变量具有 struct Student 类型所规定的结构。在定义变量的同时,进行初始化。在变量名 student1 后面的花括号中提供了各成员的值。最后用 printf 函数输出变量中各成员的值。

3.2拓展

(1)在定义结构体变量时可以对它的成员初始化。初始化列表是用花括号括起来的一些常量,这些常量依次赋给结构体变量中的各成员。注意:是对结构体变量初始化,而不是对结构体类型初始化。

C99 标准允许对某一成员初始化,如:

struct Student b = { .name = "Zhang San" };      //在成员名前有成员运算符"."

” .name ” 隐含代表结构体变量 b 中的成员 b.name。其他未被指定初始化的数值型成员被系统初始化为 0,字符型成员被系统初始化为 ‘\0’,指针型成员被系统初始化为 NULL。

(2)可以引用结构体变量中成员的值。

引用方式为

结构体变量名.成员名

例如,已定义了 student1 为 student 类型的结构体变量,则 student1.num 表示 student1 变量中的 num 成员,即 student1 的 num (学号)成员。在程序中可以对变量的成员赋值,例如:

student1.num=1001;

“.” 是成员运算符,它在所有的运算符中优先级最高,因此可以把 student1.num 作为一个整体来看待,相当于一个变量。上面赋值语句的作用是将整数 1001 赋给 student1 变量中的成员 num。

注意:不能企图通过输出结构体变量名来达到输出结构体变量所有成员的值。

下面用法不正确:

printf("%s\n", student1);        //企图用结构体变量名输出所有成员的值

只能对结构体变量中的各个成员分别进行输入和输出。

(3)如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。如果在结构体 struct Student 类型的成员中包含另一个结构体 struct Date 类型的成员 birthday ,则引用成员的方式为

student1.num;                  //结构体变量student1中的成员num
student1.birthday.month;       //结构体变量studentl中的成员birthday中的成员month

不能用 student1. birthday 来访问 student1 变量中的成员 birthday,因为 birthday 本身是一个结构体成员。

(4)对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。

例如:

student2.score = student1. score;           //赋值运算
sum = student1.score + student2.score;      //加法运算
student1.age++;                             //自加运算

由于 “.” 运算符的优先级最高,因此 student1.age++ 是对 ( student1.age ) 进行自加运算,而不是先对 age 进行自加运算。

(5)同类的结构体变量可以互相赋值

如:

student1 = student2;        //假设student1和 student2已定义为同类型的结构体变量

(6)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。

例如:

scanf("%d", &student1.num);        //输人student1.num的值
printf("%o", &student1);           //输出结构体变量student1的起始地址

但不能用以下语句整体读入结构体变量,例如:

scanf("%d,%s,%c,%d,%f,%s\n", &student1);

说明:

结构体变量的地址主要用作函数参数,传递结构体变量的地址。

3.3举例说明

举例:输入两个学生的学号,姓名和成绩,输出成绩较高的学生的学号,姓名和成绩。

#include<stdio.h>
struct Student        //声明结构体类型
{
	int num;
	char name[20];
	float score;
}student1, student2;  //定义结构体变量

int main()
{
	printf("请输入两组数据:\n");
	scanf_s("%d %s %f", &student1.num, student1.name, 20, &student1.score);
	scanf_s("%d %s %f", &student2.num, student2.name, 20, &student2.score);
	printf("成绩高的是:\n");
	if (student1.score > student2.score)
		printf("%d %s %6.2f\n", student1.num, student1.name, student1.score);
	else if (student1.score < student2.score)
		printf("%d %s %6.2f\n", student2.num, student2.name, student2.score);
	else
	{
		printf("%d %s %6.2f\n", student1.num, student1.name, student1.score);
		printf("%d %s %6.2f\n", student2.num, student2.name, student2.score);
	}
	return 0;
}

运行结果:

程序分析:

(1)student1 和 student2 是 struct Student 类型的变量。在 3 个成员中分别存放学号、姓名和成绩。

(2)用 scanf_s 函数输入结构体变量时,必须分别输入它们的成员的值,不能在 scanf_s 函数中使用结构体变量名一揽子输入全部成员的值。注意在 scanf_s 函数中在成员 student1.num 和 student1.score 的前面都有地址符 &,而在 student1.name 前面没有 &,这是因为 name 是数组名,本身就代表地址,故不能再加一个&。

(3)根据 student1.score 和 student2.score 的比较结果,输出不同学生的信息。从这里可以看到利用结构体变量的好处:由于 student1 是一个 “ 组合项 ”,内放有关联的一组数据,student1.score 是属于 student1 变量的一部分,因此如果确定了 student1.score 是成绩较高的,则输出 student1 的全部信息是轻而易举的,因为它们本来是互相关联,捆绑在一起的。如果用普通变量则难以方便地实现这一目的。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年12月27日 下午7:54
下一篇 2023年12月27日

相关推荐