结构体
在实际的编程过程中,往往一组数据中包含有不同的数据类型。而简单的数据类型和数组都不能存储一组不同数据类型的数据,为了解决这一问题。C语言提供了复杂类型数据结构。复杂数据结构包括结构体和联合体。这是一个可以由用户自己定义的一个数据结构。
结构体是不同数据类型组成的数据联合,结构体也可以像数组一样整体使用,也可以对结构体成员单个使用。
结构体的成员变量不仅可以包含基本数据类型和数组,也可以嵌套结构体。例如定义了一个结构体a,定义了一个结构体b,结构体a可以作为结构体b的成员存在。
1 结构体和结构体变量的定义
结构体有三种定义方式:
1)常见的先定义结构体,这后定义结构体变量
例如 :
struct std{
char name[20];
int nub;
};
struct std std1,std2;
这种方式是首先定义一个结构体std,在需要使用结构体变量的时候通过struct std 作为结构体类型,定义结构体变量std1和std2。
2)在定义结构体类型的时候顺便定义结构体变量
例如:
struct std{
char name[20];
int nub;
} std1,std2;
这种方式是在定义结构体类型的时候顺便定义了结构体变量std1和std2.在后面需要使用结构体的时候不需要定义,直接使用std1和std2即可。
3)直接说明结构体变量
例如:
struct {
char name[20];
int nub;
} std1,std2;
这种方式定义的结构体只能通过std1和std2两个结构体变量进行操作,而不能在定义其他的结构体变量
2 结构体的初始化
结构体变量和其他的变量一样可以在定义的时候初始化,根据结构体的定义不同,结构体初始也有几种不同的初始化方式。
1)在定义完结构体后,定义结构体变量的时候初始化
例如 :
struct std{
char name[20];
int nub;
};
struct std std1,std2 = {“ zhangsan”,100};
2)在定义结构体时顺便定义结构体变量的时候初始化
例如:
struct std{
char name[20];
int nub;
} std1,std2 = {“ zhangsan”,100};
直接说明定义结构体变量的定义方式也可以使用第二种初始化方式进行初始化。
3)这种初始方式不区分定义的方式,针对结构体内部元素的初始化。我们可以顺序对结构中的元素一一进程初始化,这种初始化方式可以使用结构体中的成员名,也可以不使用结构体中的成员名。
例如:
struct std{
char name[20];
int nub;
double dd;
};
truct std std2 = {“ zhangsan”,100,88.8};//不带成员名初始化
truct std std2 = {name =“ zhangsan”,nub = 100,dd = 88.8}; //带成员名初始化
而在结构体初始化的时候我们也可以初始化结构体中的部分成员,那么这种初始化的时候就需要带成员变量初始化了。
例如:
struct std{
char name[20];
int nub;
double dd;
};
truct std std2 = {name =“ zhangsan”,dd = 88.8}; //带成员名部分初始化
如果不带成员名称,那么初始化的时候会默认根据结构体中成员的顺序进行初始化,如果数据类型对应不上则会报错。
3 结构体的使用
结构体处理在文件传输的时候、初始化和相同类型的结构变量相互赋值时候是整体使用的。其他在获取结构体中成员值和对结构体的成员赋值的时候一般是单独使用结构体成员。而结构体和结构体成员之间用哪个”.”连接来操作结构体成员。
使用方法如下:
struct std{
char name[20];
int nub;
double dd;
};
truct std std2 = {“ zhangsan”,100,88.8};
//获取结构体中的成员值
char name1[20] = std2.name;
int aa = std2.nub;
double cc = std2.dd;
//以上操作方式是将结构体中的name、nub的dd中的赋值给name1、aa和cc。
//给结构体中的成员赋值
char name1[20] = “zhangsan”;
std2.name = name1;
int aa = 100;
std2.nub = aa;
double cc = 88.8;
td2.nub = cc;
//以上操作方式是给结构体中的name、nub的dd中的赋值name1、aa和cc。
4 结构体数组
结构体数组指的是存储相同数据类型的结构体变量的集合。这个集合中每一个元素都是结构体类型的元素。
1) 结构体数组的定义:
结构体数组的定义和结构体变量定义一样,
(1)是常见的先定义结构体,这后定义结构体数组并初始化
例如 :
struct std{
char name[20];
int nub;
};
struct std std1[5];//定义一个结构体类型为std的结构体数组std1,std1中可以存储个元素。
(2)在定义结构体类型的时候顺便定义结构体数组并初始化
例如:
struct std{
char name[20];
int nub;
} std1[5];//定义一个结构体类型为std的结构体数组std1,std1中可以存储个元素。
(3)直接说明结构体数组并初始化
例如:
struct {
char name[20];
int nub;
} std1[5];//定义一个结构体类型为std的结构体数组std1,std1中可以存储 //个元素。
1) 结构体数组的初始化
结构体数组的初始化也是根据结构体数组的定义有不同的方式:
1)常见的先定义结构体,这后定义结构体数组
例如 :
struct std{
char name[20];
int nub;
};
struct std std1[5] ={{“zhangsan”,100},
{“lisi”,101},
{“wangmazi”,102},
{“zhaowu”,103},
{“chengliu”,104}
);//定义一个结构体类型为std的结构体数组std1并初始化。
(2)在定义结构体类型的时候顺便定义结构体变量
例如:
struct std{
char name[20];
int nub;
} std1[5] ={{“zhangsan”,100},
{“lisi”,101},
{“wangmazi”,102},
{“zhaowu”,103},
{“chengliu”,104}};//定义一个结构体类型为std的结构体数组std1并初始化。
(3)直接说明结构体变量
例如:
struct {
char name[20];
int nub;
} std1[5]{{“zhangsan”,100},
{“lisi”,101},
{“wangmazi”,102},
{“zhaowu”,103},
{“chengliu”,104}};//定义一个结构体类型为std的结构体数组std1并初始化。
2)结构体数组的使用
结构体数组的使用和其他数组的使用方式一样,首先将数组中的结构体元素通过下标找到。之后找到的结构体元素的变量,将结构体变量和成员之间用”.”连接。
例如:
struct std{
char name[20];
int nub;
};
struct std std1[5] ={
{“zhangsan”,100},
{“lisi”,101},
{“wangmazi”,102},
{“zhaowu”,103},
{“chengliu”,104}
};
如上例子,先定义一个结构体,之后在定义一个结构体数组std1。如果需要使用std1结构体数组。
std1[0].name = “zhangsan”;//将结构体数组中的第一个结构体变量的成员name赋 //值为zhangsan.
std1[0].nub = 100; //将结构体数组中的第一个结构体变量的成员nub赋值为100
std1[1].name = “lisi”;//将结构体数组中的第二个结构体变量的成员name赋 //值为lisi.
std1[1].nub = 101; //将结构体数组中的第二个结构体变量的成员nub赋值为101
char na[20] ;
strcpy(na,std1[0].name); //将结构体变量中的第一个结构体变量的name赋值给字符数组na;
Int nn = std1[0].nub; //将结构体变量中的第一个结构体变量的nub赋值个nn。
因此获得第二个结构体变量中成员的值的话只需要通过下标找到对应的结构体变量,之后在通过”.”获取结构体成员的值
int nn1 = std1[1].nub;
5 结构体指针
结构体指针指的是一个指向结构体变量的指针变量。他和数组指针、函数指针一样都是指向数据集合的首地址。
在使用结构体的时候可以通过结构体指针间接的操作结构体中的成员。
1)结构体指针的定义
结构指针的定义格式如下:
struct 结构体名 *结构体指针变量名;
struct 代表定义的是一个结构体指针
结构体名为要定义的结构体类型的名称
* 代表的是定义一个指针变量
结构体指针变量名为定义的结构体变量的名称
例如:
struct std *std1;//这就代表定义一个结构体类型为std的结构体指针,结构体指针的变量名为std1。
2)构体指针的初始化
结构体指针变量在使用的时候必须赋值才能使用,因此在定义结构体指针变量的时候可以进行初始化,初始化的方式和其他指针一样,只需要在定义的时候给结构体指针变量赋一个结构体变量的地址即可。
例如:
struct std std1;
struct std *std2 = &std1;//这是将结构体变量std1的地址赋值给定义的std2结构体指针变量。
3)结构体指针的使用
结构体指针变量的使用和结构体变量一样,可以单独对结构体中的成员进行赋值。其访问的一般形式为:
(*结构指针变量).成员名
或为:
结构指针变量->成员名
第一种访问形式很明确,首先是通过“.”访问结构体成员,之后将结构体指针变量中的地址通过*取出地址的值。
第二种是直接哟结构体指针变量访问结构体成员。之间是使用”->”符号连接。而使用”->”连接之后的的成员名是指向次成员所在地址的首地址。
例如:
struct std{
char name[20];
int nub;
};
struct std std1;
struct std *std2 = &std1;
第一种通过点取出成员的值
char na[20] = std.name ;//通过结构体变量取出结构体成员的值
char na[20] = *(std1.name) ;//通过结构体指针变量取出结构体中成员的值
char na[20] = std->name ;//通过结构体指针变量取出结构体中成员的值
以上这三种方式取出的成员的值是一样的,因此可以根据不同的需求对结构体中的成员进行操作。对结构体成员赋值的方式只需要将其逆置就可以了。
6 指向结构体数组的指针
指针变量可以指向一个结构数组,这时结构指针变量的值是整个结构数组的首地址。结构指针变量也可指向结构数组的一个元素,这时结构指针变量的值是该结构数组元素的首地址。
设p为指向结构数组的指针变量,则ps也指向该结构数组的0号元素,ps+1指向1号元素,ps+i则指向i号元素。这与普通数组的情况是一致的。
指向结构体数组的指针的使用和其他的指针结构体指针变量一样,只是结构体数组指针指向的地址是数组中第一个结构体元素的地址。如果要使用数组中的第二个结构体元素的值的话只需要将指向结构体数组的指针加1即可。依次类推就可以操作整个结构体数组中的值。
例如:
struct std{
char name[20];
int nub;
};
struct std std1[5] ={
{“zhangsan”,100},
{“lisi”,101},
{“wangmazi”,102},
{“zhaowu”,103},
{“chengliu”,104}
};
struct std *std2 = &std1;//std指向结构体数组中的首地址。
char na[20] = std2->name;//通过指向结构体数组的指针获取结构体数组的第一个结构体成员的name元素的值。
std2 ++;将指向结构体数组的指针的值加1,表示std2指向结构体数组中的第二个结构体成员地址。等价于std2 = &std1[1]。
char na1[20] = std2->name;//通过指向结构体数组中第二个结构体变量中name成员的值。
7 结构体类型作为函数参数和返回值
1)在C语言中结构体类型的变量也可以作为函数参数或者函数的返回值使用。使用结构体变量作为函数参数会将C语言中的所有成员传递过去通过传递的形参名通过“.”调用结构体中的成员。
例如:
#include <stdio.h>
#include <stdlib.h>
struct std{
char name[20];
int nub;
};
void ave(struct std ps);
main()
{
struct std std1 = { "zhangsan", 100 };//定义一个结构体变量并初始化
ave(std1);//将结构体变量作为实参传递给其他函数。
}
void ave(struct std ps)
{
char na[20] = ps.name;//通过指向结构体数组的指针获取结构体数组的第一个结构体成员的name元素的值
printf("%s\n", ps.name);
}
2)结构体成员作为函数的返回值同样跟其他变量作为函数返回值一样,只不过返回的是一个结构体类型的变量,这个变量包含了返回结构体中所有的成员变量,可以通过返回的结构体获取其中的值将函数处理之后的数据传递到其他函数。
例如:
#include <stdio.h>
#include <stdlib.h>
struct std{
char name[20];
int nub;
};
struct std ave(struct std ps);
struct std std1 = { "zhangsan",100 };//定义一个结构体变量并初始化
main()
{
struct std std2 = ave(std1);//将结构体变量作为实参传递给其他函数。
printf("name = %s\n", std2.name); //输出返回结构体中name中的值
printf("nub= %d\n", std2.nub); //输出返回结构体中的name中的值
system("pause");
}
struct std ave(struct std ps)
{
strcpy(ps.name, "lisi");//修改传递过来的std1结构体中的name,将其修改为lisi
ps.nub = 101; //修改传递过来的std1结构体中的nub,将其修改为101构体成员的name元素的值
return ps;//返回修改之后的结构体
}
输出结构为:
name = lisi
nub = 101
7 结构体指针作为函数参数和返回值
1)在ANSI C标准中允许用结构变量作函数参数进行整体传送。但是这种传送要将全部成员逐个传送,特别是成员为数组时将会使传送的时间和空间开销很大,严重地降低了程序的效率。因此最好的办法就是使用指针,即用指针变量作函数参数进行传送。这时由实参传向形参的只是地址,从而减少了时间和空间的开销。
结构体指针作为函数参数进行传递和普通的指针变量传递一样,传递过去的地址形参使用的时候和结构体指针的使用一样。
例如:
#include <stdio.h>
#include <stdlib.h>
struct std{
char name[20];
int nub;
};
void ave(struct stu *ps);
main()
{
struct std std1 = { "zhangsan", 100 };//定义一个结构体变量并初始化
struct std *ps;//定义一个结构体指针
ps = &std1;//将结构体变量std1的地址赋值给结构体指针变量pa
ave(ps);//将结构体指针变量作为实参传递给其他函数。
}
void ave(struct std *ps)
{
char na[20] = ps->name;//通过指向结构体数组的指针获取结构体数组的第一个结构体成员的name元素的值
printf("%s", ps->name);
}
如以上例子所示,首先定义一个std的结构体,之后在主函数中定一个结构体变量和结构体指针变量。再将结构体中变量的地址赋值给结构体指针。通过结构体指针将std1中的数据传递给ave函数。在ave函数中通过传递的结构体指针获取std1中name成员的值。最后na中存储的值为zhangsan。
2)结构体指针作为函数的返回值。结构体指针作物函数的返回值和结构体变量作为函数的返回值一样,只不过是在使用返回的结构体指针变量的时候,不能通过“.”来获取结构体中成员值,而是使用“->”来获取结构体中的成员值。
例如:
#include <stdio.h>
#include <stdlib.h>
struct std{
char name[20];
int nub;
};
struct std *ave(struct std *ps);
struct std std1 = { "zhangsan",100 };//定义一个结构体变量并初始化
main()
{
struct std *std2 = ave(&std1);//将结构体变量作为实参传递给其他函数。
printf("name = %s\n", std2->name); //输出返回结构体中name中的值
printf("nub = %d\n", std2->nub); //输出返回结构体中的name中的值
system("pause");
}
struct std *ave(struct std *ps)
{
strcpy(ps->name, "lisi");//修改传递过来的std1结构体中的name,将其修改为lisi
ps->nub = 101; //修改传递过来的std1结构体中的nub,将其修改为101
//构体成员的name元素的值
return ps;//返回修改之后的结构体指针
}
输出结果为:
name = lisi
nub = 101
联合体
在介绍结构体的时候介绍了联合体,联合体又被称为共用体,也是程序可以自己定义的一种复杂数据类型。联合体和结构体都可以存储不同数据类型的数据,但是联合体在使用的时候是根据联合体中最大的数据类型而进行空间开辟的。而结构体是根据结构体中所有成员占有空间的大小开辟的。联合体和结构体在使用上也不同,结构体可以对每一个成员进行赋值。而结构体在使用的时候只能对其中的某一个成员赋值,如果对已经赋值的联合体再次进行赋值,那么以前对联合体赋的值将被覆盖。
1 联合体的定义
联合体(共用体)的定义:union 共用体名称 { 成员列表 }共用体变量名;
联合体的定义方式和结构体一样有三种方式:
1)常见的先定义联合体,这后定义联合体变量
例如 :
union std{
char name[20];
int nub;
};
union std std1,std2;
这种方式是首先定义一个联合体std,在需要使用联合体变量的时候通过struct std 作为联合体类型,定义联合体变量std1和std2。
第二种方式是在定义联合体类型的时候顺便定义联合体变量
例如:
union std{
char name[20];
int nub;
} std1,std2;
这种方式是在定义联合体类型的时候顺便定义了联合体变量std1和std2.在后面需要使用联合体的时候不需要定义,直接使用std1和std2即可。
3)直接说明联合体变量
例如:
union {
char name[20];
int nub;
} std1,std2;
这种方式定义的联合体只能通过std1和std2两个联合体变量进行操作,而不能在定义其他的联合体变量
2 联合体的初始化
联合体的出初始向相比结构体来说相对简单,联合体只能初始化联合体中第一个成员的值,其他成员的值无法进行初始化。初始化的方式和结构体一样更具联合体变量不同的定义方式,联合体的初始化方式不一样。
1)定义完联合体后,定义联合体变量的时候初始化
例如 :
struct std{
char name[20];
int nub;
};
struct std std1,std2 = {“ zhangsan”};
第二种是在定义联合体时顺便定义联合体变量的时候初始化
例如:
struct std{
char name[20];
int nub;
} std1,std2 = {“ zhangsan”};
直接说明定义结构体变量的定义方式也可以使用第二种初始化方式进行初始化。
2 联合体的使用
联合体的使用和结构体一样,联合体和成员之间使用“.”连接。
union std{
char name[20];
int nub;
double dd;
};
union uct std std2 = {“ zhangsan”};
1) 获取联合体中的成员值
char name1[20] = std2.name;
int aa = std2.nub;
double cc = std2.dd;
以上操作方式是将联合体中的name、nub的dd中的赋值给name1、aa和cc。
2) 给联合体中的成员赋值
char name1[20] = “zhangsan”;
std2.name = name1;
int aa = 100;
std2.nub = aa;
double cc = 88.8;
td2.nub = cc;
以上操作方式是给联合体中的name、nub的dd中的赋值name1、aa和cc。
注意:联合体中最后一次写入的值是最准确的,之前的值将会不准确。这是因为后写入的数据可能架构之前写入的数据部分覆盖了。
枚举
枚举类型是一个集合类型,将枚举变量的类型限制在一个范围之内。枚举类型的元素取值不能超过定义的范围。需要注意的是枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。枚举集合中的元素是一些命名的整形变量,元素之间用逗号隔开。
枚举一般用于流程控制,一般和switch联合使用。
枚举在使用的时候必须要赋值,类型必须为枚举定义的类型
1 枚举的定义
枚举类型的定义格式如下:enum 枚举名{ 枚举值表 };
例如:
enum color{red,yellow,blue};//这是定义一个关于颜色的枚举类型,元素为red、 //yellow和blue.
枚举变量的定义:
和结构体、联合体的定义一样,都可以在枚举定义之后或者枚举定义时定义枚举变量。
例如:
1)在枚举定义之后定义枚举变量
enum color{red,yellow,blue};
enum color a,b,c;//定义枚举变量a,b,c
2)在定义枚举的时候定义枚举变量
enum color{red,yellow,blue} a,b,c;//定义枚举变量a,b,c
可以手动设定枚举成员的值,从而自定义摸一个范围内的整数。第一个枚举成员的默认值为整形的0,如果第一个成员设置了值,那后面的枚举成员的值都在前一个成员基础上加1。
例如:
enum color{red,yellow,blue};
以上例子而言,没有对举类型的成员变量定义一个整形数值,那么red的值为0,yellow的值为1,blue的值为2.
enum color{red = 3,yellow,blue};
以上例子而言,没有对举类型的成员变量red定义一个值3,那么red的值为3,yellow的值为4,blue的值为5.
2 枚举的使用
前面说过枚举类型一般用于流程控制,和switch一起使用。
例如:
#include <stdio.h>
#include <stdlib.h>
enum color
{
rad,yellow,blue
};
int main()
{
enum color col;
col = rad;//注意,在使用枚举变量之前一定要对枚举变量赋值,如果不赋值则会报错。
//col = 0;和col = red;等价
switch (col)
{
case rad:
break;
case yellow:
break;
case blue:
break;
default:
break;
}
printf("%f\n", sdt1.b);
system("pause");
return 0;
}
typedef类型
typedef类型为一种数据类型(基本类型或自定义数据类型包括结构体或者联合体)定义一个新名字,不能创建新类型。
typedef定义的一般形式为:
typedef 原类型名 新类型名
使用方法如下:
例如:
有整型量a,b,其说明如下:
int a;
其中int是整型变量的类型说明符。int的完整写法为integer,为了增加程序的可读性,可把整型说明符用typedef定义为:
例如:
typedef int INTEGER
这以后就可用INTEGER来代替int作整型变量的类型说明了。
INTEGER a,b; 它等效于: int a,b;
typedef char NAME[20]; 表示NAME是字符数组类型,数组长度为20。然后可用NAME 说明变量,如:
NAME a1; 完全等效于: char a1[20];
typedef struct str {
char name[20];
int age;
} STU;
定义STU表示stu的结构类型,然后可用STU来说明结构变量:
STU body1;