目录
1.1结构体类型
c语言提供了一种称为结构体的数据类型,可以将同一个对象的不同类型的数据组织在一个有联系的群体里。可以定义一种结构体类型,将属于同一个对象的不同类型的成员组合在一起,形成结构体类型,然后再定义该结构体类型的变量来存储、处理每个对象的信息。
1.1.1结构体类型的定义
结构体是一种自定义的数据类型,需要先定义再使用,其一般形式为:
struct 结构体名
{
成员说明列表
};
其中,成员说明列表是组成该结构体类型的所有数据的说明,若有n个成员,则每个成员均需做数据类型说明,可表示为:
{
数据类型 成员标识符1;
数据类型 成员标识符2;
数据类型 成员表示符3;
...
数据类型 成员标识符n;
};
例如,有一个学生成绩表:
struct student
{
long number;
char name[20];
char sex;
float math;
float computer;
};
说明如下:
1)结构体名和结构体成员名遵循c语言标识符的命名规则。
2)结构体成员的类型与简单变量的定义形式相同。
3)struct是关键字,不能省略。
4)“struct student”作为一个整体表示类型名,和普通数据类型一样都可以用来定义变量的类型。
5)一对{}外面最后的分号是结构体定义的结束标志,不能省略。
1.1.2结构体变量的定义
1.先定义结构体类型,再定义其变量
一般形式为:
struct 结构体名
{
成员说明表
};
struct 结构体名 结构体变量列表;
例如,前面已经定义了一个结构体类型“struct student”,可以用它来定义具有该类型的结构体变量stu1:
struct student stu1;
2.定义结构体类型的同时定义结构体变量
一般形式为:
struct 结构体名
{
成员说明表列
}变量名表列;
例如:
struct student
{
成员说明表列
}stu2;
3.不出现结构体名,直接定义结构体变量
一般形式为:
struct
{
成员说明表列
}变量名表列;
例如:
struct
{
成员说明表列
}stu3;
第三种方法只定义了结构体变量stu3,没有出现结构体名,因此,该方法不能再用来定义其他同类型的结构体变量。
说明:在一个结构体中也可以包含另一个结构体。例如:
struct date
{
int month;
int day;
int year;
};
struct student
{
long number;
char name[20];
struct date birthday; //birthday是struct date类型
char sex;
float math;
float computer;
};
1.1.3结构体变量的初始化和引用
1.结构体变量的初始化
初始化结构体变量,也就是对结构体变量的各个成员赋值。
1)对已经定义的结构体变量初始化
例如:
struct stdent stu1={10001,"Liffun",'m',78,88};
2)同时完成结构体类型定义、结构体变量定义和初始化
struct student
{
long number;
char name[20];
struct date birthday;
char sex;
float math;
float computer;
}stu2={10002,"Lifun",'f',87,89};
2.结构体变量的引用
c语言规定,具有相同类型的结构体变量可以整体相互赋值,除此之外,结构体变量每个成员的赋值、运算、输入、输出等操作都和简单类型变量一样。
1)引用结构体变量的成员
访问结构体变量的成员必须用成员运算符。其一般形式为:
结构体变量名.成员名
例如:
stu1.name,stu2.math等
若其成员本身又是一个结构体类型的变量,则在访问该结构体变量的成员时,应采取逐级访问的方法,通过成员运算符逐级找到最底层的成员再引用。如果想要得到学生的出生日期,则可以这样引用:
stu2.birthday.month,stu2.birthday.day,stu2.birthday.year
2)整体引用结构体变量
同类型的结构体变量,可以将一个结构体变量整体赋值给另一个结构体变量。例如:
strcut student stu1={10001,"Lifun",'m',78,88};
strcut student stu3;
stu3=stu1;
通过赋值,结构体变量stu3中每个成员的值和结构体变量stu1每个成员的值相同。
1.2结构体数组
一个普通变量只能存放一个数据,一个结构体变量却可以存放多个数据。在实际数据库中包含多个学生的记录信息,这些信息具有相同的结构,这种情况下,定义一个结构体数组显然要定义多个结构体变量来得更加合理。结构体数组中地每个数组元素都是结构体类型。
1.2.1结构体数组的定义
结构体数组的定义方法如下:
struct student xs[30];
它定义了一个有30个元素的结构体数组xs,数组中每个元素都是struct student类型。它也可以直接定义结构体数组,例如:
struct student
{
long number;
char name[20];
struct date birthday;
char sex;
float math;
float computer;
}xs[30];
或
struct
{
long number;
char name[20];
struct date birthday;
char sex;
float math;
float computer;
}xs[30];
1.2.2结构体数组的初始化
结构体数组的初始化是对结构体数组元素成员的初始化,形式和多维数组的初始化类似。其一般形式是在定义结构体数组的后面加上:
={初值列表};
例如:
struct student xs[30]={{10001,"Lifun",'m',78,88},{10002,"Wangli",'f',89,90}};
1.2.3结构体数组的引用
结构体数组的引用也是引用结构体数组元素的成员,与普通数组引用类似,其一般形式为:
数组名[下标].成员名
例如:
xs[0].number //引用第一个学生的学号
xs[1].name //引用第二个学生的名字
1.3指向结构体的指针
指向结构体的指针可以指向结构体变量,也可以指向结构体数组中的某个元素;还可以定义一个指针变量,用来指向一个结构体变量。这个指针变量就指向该结构体变量,成为指向结构体变量的指针变量。
1.3.1指向结构体变量的指针
结构体指针变量定义一般形式为:
struct 结构体名 *结构体指针变量名;
例如:
struct student
{
long number;
char name[20];
struct date birthday;
char sex;
float math;
float computer;
};
struct student stu1;
struct student *p=&stu1;
定义了结构体变量指针后,如何访问该指针变量指针指向的结构体成员呢?可以使用下面两种运算符。
1)圆点运算符
一般形式为:(*结构体指针变量名).成员名。
例如,(*p).name,表示指针变量p指向结构体变量stu1的name成员。
2)指向运算符(也称箭头运算符)
一般形式为:结构体指针变量名->成员名。
例如,p->name,表示指针变量p指向结构体变量stu1的name成员。
1.3.2指向结构体数组成员的指针
指针变量可以指向普通数组,同样,指针变量也可以指向结构体类型的数组。例如:
struct student
{
long number;
char name[20];
struct date birthday;
char sex;
float math;
float computer;
};
struct student stu[20];
struct student *p=stu;
1.4结构体与函数
结构体类型与普通数据类型一样,既可以定义结构体类型的变量、数组、指针,也可以将结构体类型的数据作为函数的参数或函数的返回值。
1.4.1结构体作函数参数
结构体作函数参数,既可以把结构体变量作为函数参数,实现值传递,也可以把指向结构的指针作为函数参数,实现地址传递。
值传递,结构体变量作函数参数:
#include <stdio.h>
struct date
{
int nian;
int yue;
int ri;
};
int riqi(struct date a) //结构体变量作函数参数
{
a.nian=2018;
a.yue=5;
a.ri=22;
return 0;
}
int main()
{
struct date b;
b.nian=2017;
b.yue=4;
b.ri=26;
printf("调用前:%d %d %d\n",b.nian,b.yue,b.ri);
riqi(b);
printf("调用后:%d %d %d\n",b.nian,b.yue,b.ri);
return 0;
}
程序运行结果为:
调用前:2017 4 26
调用后:2017 4 26
通过这个例子,可以看到,在自定义函数riqi()中,对形参a的成员的修改,不会影响到主调函数中实参b的成员的值。这也是值传递的方式。
地址传递,结构体指针变量作函数参数
#include <stdio.h>
struct date
{
int nian;
int yue;
int ri;
};
int riqi(struct date *p) //结构体指针变量作函数参数
{
(*p).nian=2018;
(*p).yue=5;
(*p).ri=22;
return 0;
}
int main()
{
struct date b;
b.nian=2017;
b.yue=4;
b.ri=26;
printf("调用前:%d %d %d\n",b.nian,b.yue,b.ri);
riqi(&b);
printf("调用后:%d %d %d\n",b.nian,b.yue,b.ri);
return 0;
}
程序运行结果为:
调用前:2017 4 26
调用后:2018 5 22
通过这个人例子,可以看到,在自定义函数riqi()中,对形参p所指的结构体成员的修改,会改变主调函数中实参b的成员的值。这也是地址传递方式的特点。
1.4.1结构体作函数返回值
函数的返回值除了可以是整形、实型、字符型和指向这些数据类型的指针以外,还可以返回结构体类型的值。
例如,超市清点现有物品情况,有5种商品,要求从键盘输入每种商品的名称、数量、单价,编写程序能实现以下功能:输出库存清单(包括商品名称、数量、单价、合计),以及所有商品的总金额。程序用一个函数实现“输入库存商品信息”功能,其返回值为结构体类型:
#include <stdio.h>
struct StoreLib
{
char name[12];
int num;
float price;
float SumMoney;
};
struct StoreLib Goods;
float Total=0;
int list(struct StoreLib Goods)
{
Goods.SumMoney=Goods.num*Goods.price;
Total=Total+Goods.SumMoney;
printf("%-24s%d\t%.2f\t%.2f\n",Goods.name,Goods.num,Goods.price,Goods.SumMoney);
return 0;
}
struct StoreLib InputInfo()
{
scanf("%s",&Goods.name);
scanf("%d%f",&Goods.num,&Goods.price);
return Goods;
}
int main()
{
struct StoreLib iGoods[5];
int i;
printf("请输入5种库存商品:商品名称 数量 单价\n");
for(i=0;i<5;i++)
iGoods[i]=InputInfo();
printf("\n---------------------------------------\n");
printf("库存清单:\n");
printf("商品名称\t\t数量\t单价\t小计\n");
for(i=0;i<5;i++)
list(iGoods[i]);
printf("库存商品金额总计:%.2f\n",Total);
return 0;
}
1.5共用体
根据实际情况,有时需要把几种类型不同的数据,如一个整型变量、一个字符变量、一个实型变量存放在起始地址相同的同一段存储单元种。这三个变量在内存种所占的字节数不同,但都从同一个地址开始存放。这种几个类型不同的变量共同占用同一段内存的结构,称为“共用体”类型结构。共用体,也称为联合体。
1.5.1共用体类型及其变量的定义
共用体的定义方法和定义结构类型相似,其一般形式为:
union 共用体名
{
成员表列
};
union是关键字,是定义共用体类型必不可少的。一对{}种的内容为共用体成员的类型说明,与结构体类型的成员说明一致。例如:
union data
{
double a;
int i;
char ch
};
定义共用体变量的方法与结构体类似。
1.先定义共用体类型,再定义变量
union 共用体名
{
成员表列
};
union 共用体名 变量表列;
2.定义共用体类型的同时定义共用体变量
union 共用体名
{
成员表列
}变量表列;
3.不出现共用体名,直接定义共用体变量
union
{
成员表列
}变量表列;
定义共用体变量的3种方法种,前两种是等价的,第3种方法没有定义该共用体类型的名字,因此,该方法不能再用它来定义其他共用体变量。
而且,可以看到,“共用体”与“结构体”很相似,但两者也有区别。共用体变量的所有成员共享同一段存储空间,这个存储空间等于共用体变量种占据内存字节数最多的成员的字节数。
1.5.2共用体变量的引用
共用体变量必须先定义,才能引用,而且不能整体引用,只能引用共用体变量的成员。其一般形式为:
共用体变量名.成员名
了解共用体变量的引用:
#include <stdio.h>
union student
{
long number;
char c;
int age;
};
int main()
{
union student s1,s2;
s1.number=10001;
s1.c='L';
s1.age=65;
printf("共用体变量s1成员的值为:\n");
printf("s1.number=%d\n s1.c=%c\n s1.age=%d\n",s1.number,s1.c,s1.age);
s1.c='W';
s2=s1;
printf("共用体变量s2的值为:\n);
printf("s2 is %c\n",s2.c);
return 0;
}
1.5.3说明
1)共用体变量的几个成员放在同一段内存中,但在每一段瞬间存储单元中只能存放一个值,所以每一瞬间只有一个成员起作用。
2)利用覆盖技术,一个新的值存入后原有的成员只就会失去作用了,所以共用体变量中起作用的成员是最后一次存入的成员。因此上例中,在s1.age赋值之后,s1.num和s1.c所在的内存单元被覆盖。
3)同一类型的共用体变量之间可以整体赋值,但应注意由于公用一段内存,赋值的操作其实只发生在同一类型共用体变量的成员之间。如上例s1赋值给s2后,只有s2.c的值是确定的。
4)不能对共用体变量名赋值,也不能直接输入、输出共用体变量。
1.6枚举类型
解决实际问题时,经过分析判断,某个变量的取值只有少数几种,在c语言中,可以将这种变量定义为枚举类型。“枚举”也称“列举”,就是指把变量所有可能的取值都列举出来,变量的值只限于列举出来的值的范围内。
定义枚举类型的一般形式为:
enum 枚举变量名{取值表列};
例如:
enum response{no,yes,none};
上面的语句定义了名为response的枚举类型,取值范围为:no、yes、none。可以用该类型来定义变量,例如:
enum response answer;
当然,也可以同时定义枚举类型和枚举变量,例如:
enum response{no,yes,none} answer;
或者不出现枚举名,直接定义枚举变量:
enum {no,yes,none} answer;
说明:
1)一对{}种的取值称为枚举常量,都是整形常量,用户在定义时不指定其值,系统默认第1个枚举常量的值为0,后面的枚举值一次增加1,例如:
enum response{no,yes,none} answer; //其中no=0,yes=1,none=2
2)枚举也可以指定,例如:
enum {no=1,yes=3,none=4} answer;
1.7typedef类型
通过前面的内容,读者学习并掌握了各种系统定义的类型和用户自己定义的结构体、共用体、指针、枚举类型。同时,还可以使用typedef语句为这些基本数据类型或自定义的数据类型定义一个别名。
typedef定义的一般形式为:
typedef 原类型名 新类型名;
例如:
typedef struct student STUDENT;
或
typedef struct student
{
long num;
char name[10];
int age;
} STUDENT;
两者都是为原有结构体类型“struct student”定义一个新名字STUDENT。可以用新名字定义变量。
STUDENT stu1,stu2;
等价于
struct student stu1,stu2;
需要注意的是,typedef只是为一种已经存在的类型定义一个新的名字而已,并不是用于定义一种新的数据类型。