经过这一段时间的学习,我们在我们编写代码的时候,我们不同类型的数据用不同的方式进行存储,遇到不变的数据我们就使用常量来进行存储,遇到可能需要改变的数据我们使用变量来进行存储,遇到同类型的多个数据我们就是用数组来进行存储,如果我们遇到不同类型的多个数据怎么进行处理呢,这一节我们就专门来解决这个问题,当然我们需要先了解一下解决问题的关键是什么就是使用结构体。
结构体就是把具有内在联系的多个不同类型的数据结合成一个整体,使他们关联起来。
结构体
关于结构体
如果说,我们实现了一个学生系统,要存储学生的信息。那么如果和一名学生有关的信息,都是单独的去定义变量的话,那么维护起来,就会比较麻烦。
示例:
int main()
{
int nAge1 = 18;
double dHeight1 = 175.5;
int nSocore1 = 98;
int nAge2 = 20;
double dHeight2 = 168.5;
int nSocore2 = 95;
int nAge3 = 20;
double dHeight3 = 168.5;
int nSocore3 = 95;
int nAge4 = 20;
double dHeight4 = 168.5;
int nSocore4 = 95;
int nAge5= 20;
double dHeight5 = 168.5;
int nSocore5 = 95;
int nAge6 = 20;
double dHeight6 = 168.5;
int nSocore6 = 95;
return 0;
}
这种方式太过繁琐,相同功能的数据定义次太多,这时候,我们就需要结构体,那么到底怎么使用结构体呢,我们就继续向下看。
原始形式
结构体定义的最初的形式:
struct
{
类型名1 成员名1;
....
类型名n 成员名n;
}结构体变量名 = {初始化元素1,...初始化元素n};
示例
int main()
{
//定义结构体变量
struct {
char szName[20];
int nAge;
double dHeight;
int nScore;
int nGender;
}stcXiaomingInfo;
//使用结构体变量,是通过.来访问结构体中的成员(字段)的
//使用某一个成员的时候,它是什么类型,就按照什么类型来使用
strcpy_s(stcXiaomingInfo.szName, 20, "xiaoming");
stcXiaomingInfo.nAge = 18;
stcXiaomingInfo.dHeight = 175.5;
stcXiaomingInfo.nGender = 1;
stcXiaomingInfo.nScore = 98;
//通过键盘输入
printf("请输入年龄:\n");
scnaf_s("%d",&stcXiaomingInfo.nAge);
//打印
printf("%d",stcXiaomingInfo.nAge);
}
注:
使用结构体成员需要通过.
符号。
结构体成员的使用和普通变量的使用方法一致。
除了上面的结构体赋值方式,我们还以使用这种方式进行结构体初始化。
struct {
char szName[20];
int nAge;
double dHeight;
int nScore;
int nGender;
}stcXiaohongInfo = {"xiaohong",20};
不过上面介绍的使用结构体的方式都还比较麻烦,我么以后可以使用下面这种比较简单的方式,比较常用。
格式:struct 结构体名
{
类型名1 成员名1
...
类型名n 成员名n
};
这种写法不是为了定义一个结构体变量,而是定义了一个结构体类型的数据类型,我们可以像定义其他变量一样使用它。
struct STUINFO{
//STUINFO是类型名
char szName[20];
int nAge;
double dHeight;
int nScore;
int nGender;
};//这里可以定义结构体变量名
int main()
{
int nNum;
//定义结构体变量
struct STUINFO stcXiaomingInfo; //在C中需要在前面加上struct C++里可以不加
//使用结构体变量,是通过.来访问结构体中的成员(字段)的
//使用某一个成员的时候,它是什么类型,就按照什么类型来使用
strcpy_s(stcXiaomingInfo.szName, 20, "xiaoming");
stcXiaomingInfo.nAge = 18;
stcXiaomingInfo.dHeight = 175.5;
stcXiaomingInfo.nGender = 1;
stcXiaomingInfo.nScore = 98;
//结构体变量的初始化
STUINFO stcXiaohongInfo = {"xiaohong",20};
STUINFO stcXiaoheiInfo = { "xiaohei",20 };
//给xiaohei 从键盘上输入身高,性别,分数
printf("请输入身高");
scanf_s("%lf", &stcXiaoheiInfo.dHeight);
printf("请输入性别");
scanf_s("%d", &stcXiaoheiInfo.nGender);
printf("请输入分数");
scanf_s("%d", &stcXiaoheiInfo.nScore);
printf("请输入姓名");
scanf_s("%s", &stcXiaoheiInfo.szName,20);
//输出xiaoming和xiaohei的信息
printf("%s %d %lf %d %d", stcXiaomingInfo.szName,
stcXiaomingInfo.nAge, stcXiaomingInfo.dHeight,
stcXiaomingInfo.nGender, stcXiaomingInfo.nScore);
printf("%s %d %lf %d %d", stcXiaoheiInfo.szName,
stcXiaoheiInfo.nAge, stcXiaoheiInfo.dHeight,
stcXiaoheiInfo.nGender, stcXiaoheiInfo.nScore);
return 0;
}
我们也可以使用这种模式直接定义出来一个变量:
struct _PERSON
{
char name[20];
char sex;
int age;
float height;
}per1;
结构体的嵌套
结构体中可以定义多个种数据类型在里面,我们可以不可将结构体放进去?当然是可以的。
struct POINT
{
int x;
int y;
};
struct STUINFO
{
char szName[20];
int nAge;
double dHeight;
int nScore;
int nGender;
POINT nP;
};
int main()
{
struct STUINFO stu1;
stu1.nP.x = 1;
stu1.nP.y = 1;
printf("%d,%d",stu1.nP.x,stu1.nP.y);
//使用嵌套的结构体的时候,后面只需要加一个".",就可以继续使用这个结构体里的成员。
}
注:
结构体不能嵌套自己本身,但能够嵌套自己类型的指针
结构体变量的初始化
结构体的初始化我们在之前的例子中都已经了解过了,这里就在简单介绍一下。
结构体可以在定义的时候定义出来一个变量并初始化。
struct person
{
char name[20];
int sex;
int age;
float height;
}per = {"XiaoMing",1,20,180};
结构体赋初值的时候,将值按照顺序放在大括号中,与成员变量的顺序一一对应,用逗号分割。
结构体变量的引用
结构体的引用在我们刚才示例代码中已经用到过了,就是成员运算符.
,他在所有运算符中国优先级最高,我们对结构体变量进行引用该的时候,需要结构体变量定义之后,其一般形式为:结构体变量名.成员名
如果我们定义的结构体又嵌套了一个结构体,那我们需要连用成员运算符,直到最低一级才能进行运算,结构体中的变量都是某一种类型,所以我们可以像操作普通变量一样操作他们。
结构体数组
定义结构体数组的方法,就像我们普通的数组一样,因为结构体也是一种数据类型,接下来我们就介绍三种定义的方法:
定义结构体的时候就定义数组,不常用:
struct person
{
char name[20];
int sex;
int age;
float height;
}per[3];
定义结构体之后,使用结构体名定义,这种方式是最常见的。
struct person
{
char name[20];
int sex;
int age;
float height;
};
int main()
{
struct person per[3];
}
我们也可以直接在定义结构体的阶段对结构体数组进行赋值,这种方式和之前在定义的时候直接定义出来变量并赋值相似。
我们把数组中的元素都放到大括号里,括号中一次排列元素各个成员的初始值。这个和数组的初始化是一样的,如果给出了全部元素的值,就不用写数组的长度可以不指定,系统直接按照我们给出的元素个数作为数组长度。
struct person
{
char name[20];
int sex;
int age;
}per[3] = {{"LiMing",1,18},{"DaFei",1,20},{"XiaoLan",0,30}};
结构体数数组在内存中也是连续的,它的使用方式是数组和结构体的组合。使用结构体数组只要尊旭对数组元素的引用规则和对结构体变量成员的引用规则即可,例如:
int main()
{
struct _PEOPLE
{
char name[20];
int age;
int sex;
};
//定义一个结构体变量
_PEOPLE stcStudent[10];
for(int i = 0;i<10;i++)
{
printf("请依次输入姓名 年龄 性别(男:1,女:0):\n");
scanf_s("%s %d %d",stcStudent[i].name,&stcStudent[i],age,&stcStudent[i],sex);
}
}
注:
在C语言编译器中,定义结构体时,struct person前面的struct是必须的,但是在C++编译器中,这个可以省略。
联合体
联合体的基本语法
联合体有时候也被称为共用体,其除了关键字是union之外,其他的使用方法和结构体一模一样。
联合体的定义形式:union 联合体名
{
类型名1 成员名1
...
类型名n 成员名n
};
示例:
union _TEST
{
int a;
int b;
};
其使用方式和等和结构体相同。
联合体和结构体的区别
通过我们刚才的了解,我们发现联合体和结构体除了关键字不同,操作都是一样的啊,那要联合体有什么用呢?
其实,他们还有有区别的,区别在于它的对于数据的存储:结构体:每一个成员单独占用一个内存空间
联合体:所有成员共用一块内存空间
我们通过示例来观察一下结构体的内存情况:
struct _TEST
{
int a;
int b;
};
int main()
{
struct _TEST stc1;
st1.a = 10;
st1.b = 20;
printf("%d,%d\n",a,b);
return 0;
}
内存:
我们通过截图可以看到,在内存中,stc1的a和b是占用了不同的内存空间。
接下来我们看看联合体:
union _TEST
{
int a;
int b;
};
int main()
{
union _TEST stc1;
stc1.a = 10;
stc1.b = 20;
printf("%d,%d\n", stc1.a, stc1.b);
return 0;
}
我们在执行stc1.b = 20
这一行之前加一个断点,运行到这一行的就是就会停下,我们在这里查看一次内存:
然后我们让stc1.b = 20
这一行执行,接着查看内存:
我们可以看到,在内存中stc1的a和b是占用了相同的空间。
联合体的应用
我们了解了联合体的特性之后我们可以用来干什么呢?通常它有两种用处:
- 同一个数据需要多种表现形式
- 在互斥情况下节省空间
我们这个示例中,有小学生,初中生,和高中生,在填写信息的时候,可以选择填写家长的手机号,自己的手机号,或者自己的身份证,三个不同阶段的学生填写的数据类型可能不一样,但是需要填写一个即可,这样我们使用联合体就节省了内存空间。
#define 小学生 1
#define 初中生 1
#define 高中生 1
struct STUINFO {
char szName[20];
int nAge;
int nTpye;
union {
char szParentTel[20];
char szSelfTel[20];
char szId[20];
}SpecialInfo;
};
int main()
{
STUINFO xiaoming = { "xiaoming" };
xiaoming.nAge = 7;
xiaoming.nTpye = 小学生;
scanf_s("%s", xiaoming.SpecialInfo.szParentTel, 20);
return 0;
}
类型定义
在以后我们编写代码的时候会遇到typedef,他是用来给一个类型定义一个别名,然后我们就可以使用新的类型名来进行定义变量。
定义一个新类型名的一般格式:
typedef 类型名 标识符;
示例:
typedef int INT;
typedef int *PINT
int main()
{
int num = 10;
INT num2 = 20;
PINT pInt = NULL; //pInt是int类型指针
pInt = #
printf("%d",*pInt);
}
使用了类型定义之后,我们就可以用新的名字来代替原来的名字定义变量。
注:
使用typedef语句不是创造新类型,而是为已经存在的类型添加一个名字。
我们最常用的就是用来定义结构体:
typedef struct _TEST
{
int a;
int b;
}TEST,*PTEST; //注意,使用typedef之后,这里是新的类型名,不是创建的变量。
int main()
{
//方式一
struct _TEST stcTest = {10,20};
//方式二
TEST stcTest1 = {11,22};
return 0;
}
为什么要这样定义?
这是一个C语言的遗留问题。在C语言中,定义变量必须使用第一种方式。
为了省略一个struct,所以重定义类型名。这种用法一直用到了现在,并且遗留到了C++中。但是我们知道C++中定义结构体变量本身不需要struct,尽管如此现在很多的开发者还保留着C语言的开发习惯。