结构体与数组类似,都是由若干分量组成的,与数组不同的是,结构体的成员可以是不同类型,可以通过成员名来访问结构体的元素。
结构声明(structure declaration)描述了一个结构的组织布局。声明语法如下:
structure 结构体名 {
数据类型 成员1;
数据类型 成员2;
数据类型 成员3;
...
}
# include
#define MAXLARGE 20
// 声明一个结构体名为book的结构体
struct book
{
// 定义int类型的BookNum成员
int booknum;
// 定义char类型的Title数组成员,数组最大为MAXLARGE
char title[MAXLARGE];
// 定义char类型的author数组成员,数组最大为MAXLARGE
char author[MAXLARGE];
// 定义double类型的price成员
double price;
};
int main(void){
return 0;
}
该声明描述了一个由一个int类型变量,两个char类型变量的数组和一个double类型的结构体。该声明并未创建实际的数据对象,只描述了该对象由什么组成。
在结构声明中,yong一对花括号括起来的是结构成员列表。每个成员都用自己的声明来描述。例如:title是一个内含MAXLARGE个元素的数组。成员可以是任意一种数据类型,甚至可以是其他结构!右花括号后的分号是必须的,它表示结构布局定义结束。
结构声明可以放在函数的外部也可以放在函数的内部。如果放在函数内部,就如同局部变量一样,只限于函数的使用。如果放在外部,那在声明之后的所有函数都可以使用。
结构有两层含义。一层是结构布局,上面已经说过,结构布局是告诉编译器如何表示数据,但是它并未让编译器为数据分配空间。下一步是创建一个结构变量,即是结构体的另一层含义。
程序中创建结构变量的是:
// 声明一个结构体名为book的结构体
struct book
{
// 定义int类型的BookNum成员
int booknum;
// 定义char类型的Title数组成员,数组最大为MAXLARGE
char title[MAXLARGE];
// 定义char类型的author数组成员,数组最大为MAXLARGE
char author[MAXLARGE];
// 定义double类型的price成员
double price;
// library表示创建一个book结构体的变量名
} library;
等效于:
struct book library;
编译器执行这行代码便创建了一个结构变量library。编译器使用book模板为该变量分配空间。
声明结构的过程和定义结构变量的过程可以组合成一个步骤。则如下所示:
struct
{
// 定义int类型的BookNum成员
int booknum;
// 定义char类型的Title数组成员,数组最大为MAXLARGE
char title[MAXLARGE];
// 定义char类型的author数组成员,数组最大为MAXLARGE
char author[MAXLARGE];
// 定义double类型的price成员
double price;
} library; // 声明的右花括号后跟变量名
在结构变量的声明中,struct book所起的作用相当于一般声明中的int或float。例如:可以定义一个struct book类型的变量,甚至是指向struct book的指针。
struct book doyle, panish, *ptbook;
初始化一个结构变量与初始化数组的语法类似:
struct book library=
{
100010,
"Tom and Jerry",
"unknow",
10.00
};
结构类似一个超级数组,在这个超级数组中,可以是一个元素为char类型,下一个元素为int类型,在数组中可以使用下标单独访问数组中的各个元素。而在结构体中需要使用结构成员运算符 ------( . )访问结构体中的成员。
# include
#define MAXLARGE 20
struct book
{
int booknum; // 定义int类型的BookNum成员
char title[MAXLARGE]; // 定义char类型的Title数组成员,数组最大为MAXLARGE
char author[MAXLARGE]; // 定义char类型的author数组成员,数组最大为MAXLARGE
double price; // 定义double类型的price成员
};
struct book library=
{
100010,
"Tom and Jerry",
"unknow",
10.00
};
int main(void){
// 在主函数中,使用结构体成员操作符访问结构体成员的属性。
printf("%s\n", library.title);
printf("%d", library.booknum);
return 0;
}
C99和C11为结构体提供了指定初始化器,其语法与数组的指定初始化器类似。但是,结构的指定初始化器使用点运算符和成员名标识特定的元素。
# include
#define MAXLARGE 20
struct book
{
int booknum; // 定义int类型的BookNum成员
char title[MAXLARGE]; // 定义char类型的Title数组成员,数组最大为MAXLARGE
char author[MAXLARGE]; // 定义char类型的author数组成员,数组最大为MAXLARGE
double price; // 定义double类型的price成员
};
// 使用C99和C11结构的初始化器初始成员,也可以只初始化部分成员。
struct book ptbook=
{
.booknum=10010,
.title="Tom",
.author="unknow",
.price=12.00
};
int main(void){
printf("%s\n", ptbook.title);
printf("%d", ptbook.booknum);
return 0;
}
结构数组本质上是一个数组,其数组中的各个元素都是结构体。
声明结构数组和声明其他类型的数组类似,声明结构数组如下:
struct book arrayst[MAXARRAY];
以上代码把arrayst声明为一个内含MAXARRAY个元素的数组。数组的每个元素都是一个book类型的结构。
数组名arrayat本身不是结构名,他是一个数组名,该数组中的每个元素都属struct book类型的结构变量。
#include
#define MAXLARGE 20
struct book
{
int booknum; // 定义int类型的BookNum成员
char title[MAXLARGE]; // 定义char类型的Title数组成员,数组最大为MAXLARGE
char author[MAXLARGE]; // 定义char类型的author数组成员,数组最大为MAXLARGE
double price; // 定义double类型的price成员
};
struct book arrayst[3]={
{100011, "围城", "钱钟书", 100.00},
{100012, "西游记", "吴贯中", 102.00},
{100013, "红楼梦", "曹雪芹", 103.00}
};
int main(void){
printf("%s\n", arrayst[0].title);
printf("%d", arrayst[1].booknum);
return 0;
}
arrayst[1].num表示访问数组中的第二个元素,也就是第二个结构体中的num属性的值。
在一个结构中包含另一个结构(即嵌套结构)
示例:
#include
// 第一个结构体
struct names
{
char first_name[20];
char last_name[20];
};
// 第二个结构体
struct friends {
// 嵌套第一个结构体
struct names people;
int age;
char gender;
// 初始化第二个结构体
} info={
{"Tom", "jerry"},
12,
'm'
};
int main(void){
printf("%s\n", info.people);
printf("%d", info.age);
return 0;
}
注意如何在结构声明中创建嵌套结构体。和声明int类型变量一样:
struct names people;
该声明中people是一个struct names类型的变量。当然,文件中也应包含结构names的声明。
访问嵌套结构的成员时需要使用两次点运算符:
printf("%s\n", info.people.last_name);
#include
#define MAXLARGE 20
struct names
{
char first_name[20];
char last_name[20];
};
struct friends {
struct names people;
int age;
char gender;
};
struct friends info[2]={
{{"Tom", "jerry"}, 12, 'm'},
{{"John", "Tim"}, 11, 'f'}
};
int main(void){
// 声明一个结构指针
struct friends *him;
// 结构指针指向info的第1个元素
him = &info[0];
printf("%s\n", him->people.last_name);
printf("%d", him->age);
return 0;
}
在上面的程序中,声明结构指针很简单:
struct friends *him;
首先是struct关键字,其次是标记结构friends,然后是一个星号,其后跟指针名。
和数组不同的是,结构变量并不是结构变量的地址,因此要在结构变量名前加上&运算符。
该声明并未创建一个新的结构,但是指针him现在可以指向任意现有guy类型的结构。在本例中,info是一个结构数组,这意味info[0]是一个结构,所以,要让him指向info[0],可以这样写:
him = &info[0];
him指向info[0],则him+1指向info[1]。
在上述程序中him指向info[0],在操作符一节中我们使用 -> 操作符获取结构指针成员的值。
// 获取him指向的成员属性值
printf("%s\n", him->people.last_name);
printf("%d", him->age);
ANSI C允许把结构作为参数使用。所以程序员可以选择是传递结构本身,还是传递指向结构的指针。如果你只关心结构中的某一部分,也可以把结构的成员作为参数。接下来解析这三种传递方式。
传递结构成员就是把结构体成员属性的值传递到函数的参数中,完成函数的功能。
#include
// 定义结构体
struct funds
{
double balance;
double savefunds;
};
// 定义相加函数
double sum(double x, double y){
return x + y;
}
int main(void){
// 初始化结构体
struct funds per={
1000,
1000
};
//调用函数,函数的参数为结构体的成员属性
printf("The total balance is : %lf", sum(per.balance, per.savefunds));
return 0;
}
接上面的内容,下面将结构的地址传递给函数。
#include
// 定义结构体
struct funds
{
double balance;
double savefunds;
};
// 定义加法函数。声明形参为结构体地址。形参地址为money
double sum(const struct funds * money){
return (money->balance + money->savefunds) ;
}
int main(void){
// 初始化结构体
struct funds per={
1000,
1000
};
// 将实参结构体地址传入函数。
printf("The total balance is : %lf", sum(&per));
return 0;
}
对于支持将结构传递给函数的编译器可以把结构体当作参数进行传递。
#include
// 定义一个结构体
struct funds
{
double balance;
double savefunds;
};
// 定义相加函数,并将结构体传给函数
double sum(struct funds totalbal){
return (totalbal.balance + totalbal.savefunds) ;
}
int main(void){
// 初始化结构体
struct funds per={
1000,
1000
};
// 将结构体传给函数使用
printf("The total balance is : %lf", sum(per));
return 0;
}
传递结构和传递结构指针各有各的优缺点。把指针作为参数时执行起来很快,只要传递一个地址。缺点是无法保护数据,不过新增的const限定符解决了这个问题。
把结果作为参数的优点是函数处理的是原始数据的副本,这保护了原始数据。缺点是较老的版本可能无法实现,而且传递结构浪费时间和空间。尤其是把大型结构传递给函数。
在C语言中,允许几种不同类型的变量存放到同一段内存单元中(不是同时存储),其典型用法是设计一种表以存储无规律、事先也不知道顺序的混合类型。使用联合类型的数组,其中的联合都大小相等,每个联合可以存储各种数据类型。
创建联合和创建结构的方式相同,需要一个联合模板和联合变量。用关键字union声明联合
union hold{
int dight;
double big;
char litter;
}
下面是联合的一些用法:
union hold fit;
fit.dight = 10; // 把10存储在fit的dight中
fit.big = 2.1 // 清除10,存储2.0
fit.litter = 'w' // 清除2.0 存储w
点运算符表示正在使用哪种数据类型。在联合中,一次只能存储一个值。即时有足够的空间,也不能同时存储一个char类型值和一个int类型的值。所以编写代码时要注意当前存储的是哪一种数据类型。
和用指针访问结构使用一样,用指针访问联合时也要使用 ->运算符
pu = &fit;
int x = pu->dight; // 相当于fit.dight