1,结构体
把有内在联系的不同变量组织起来,封装成一个整体,这个整体就是结构体(structure)
1.1,结构体类型的声明
关键字:struct
语法:
struct 结构体名{
成员列表;
};
*注意:每个成员后要加“;”,结构体的大括号后也要加“;”
例:构造一个商品(product)的结构体类型。
struct Product{ /*声明商品结构体*/
/*成员名前的首字母表示该成员的类型*/
char cName[50]; /*商品名称*/
char cShape[30]; /*商品形状*/
char cColor[20]; /*商品颜色*/
char cFunc[50]; /*商品功能*/
int iPrice; /*商品价格*/
};
*注意:此时只是定义了一个结构体类型,并未分配其内存单元。简单说就是声明了一种像int,char,double等的类型,还需定义变量才能分配内存单元,才能用。
1.2,结构体变量的定义
定义结构体全局变量的三种方式
1,先声明结构体,在定义变量
struct Product{ /*声明商品结构体*/
/*成员名前的首字母表示该成员的类型*/
char cName[50]; /*商品名称*/
char cShape[30]; /*商品形状*/
char cColor[20]; /*商品颜色*/
char cFunc[50]; /*商品功能*/
int iPrice; /*商品价格*/
};
struct Product product1; /*定义变量商品1(product1)*/
struct Product product2; /*定义变量商品2(product2)*/
2,声明结构体的同时定义变量
struct Product{ /*声明商品结构体*/
/*成员名前的首字母表示该成员的类型*/
char cName[50]; /*商品名称*/
char cShape[30]; /*商品形状*/
char cColor[20]; /*商品颜色*/
char cFunc[50]; /*商品功能*/
int iPrice; /*商品价格*/
}product1,product2; /*定义变量商品1(product1),定义变量商品2(product2)*/
3,直接定义结构体类型变量,不需要给出结构体名称
struct{ /*声明商品结构体*/
/*成员名前的首字母表示该成员的类型*/
char cName[50]; /*商品名称*/
char cShape[30]; /*商品形状*/
char cColor[20]; /*商品颜色*/
char cFunc[50]; /*商品功能*/
int iPrice; /*商品价格*/
}product1,product2; /*定义变量商品1(product1),定义变量商品2(product2)*/
变量内存的计算:成员1+成员2+....
例如:product1的内存单元 = 50+30+20+50+4
结构体的每个变量的内存单元都是相等的
1.3,结构体变量的引用
引用形式:结构体变量名.成员名
product1.cName = "phone"; /*将产品1的名字改为phone*/
product1.iPrice = 3999; /*将价格设置为3999*/
对结构体变量惊喜赋值,存取,运算,其实就是对结构体成员进行操作
*注意:不能直接将结构体变量作为一个整体进行输入输出。输入输出也需要像引用形式 “结构体变量名.成员名” 进行输入输出。
如果像下边这种情况:成员本身又属于一个结构体类型。则需要找到最低级成员才能进行操作
例如:
struct data{
int iYear; /*年*/
int iMonth; /*月*/
int iDay; /*日*/
};
struct student{
char cName[20]; /*姓名*/
int iNum; /*学号*/
char cSex; /*性别*/
int iAge; /*年龄*/
struct data birthday; /*生日*/
}student1,student2;
给姓名,学号,年龄,性别赋值时是用:student1.cName = "xxxx"; student1.iNum = 123456等
而要给生日赋值则需要:student1.birthday.iYear = 2025;
生日 与 学号 的赋值相比较,生日 多出了一个data结构体变量 birthday
*注意:输出与赋值一样,怎么赋值的就得怎么输出。不能用结构体变量直接输出所有数据
1.4,结构体类型的初始化
定义常规变量(如int,char)时,可以直接给变量赋值如:int x = 6; 。结构体类型的变量也一样,在定义结构体变量时直接给结构体变量中的每个成员赋值。这个过程就叫结构体初始化。
struct Student{
int iAge;
char cName[64];
int iyear;
}Student1 = {19 , "LiHua" , 4}; /*将Student1.iAge = 19 ;Student1.cName = "LiHua" ;Student1.iyear = 4*/
*注意:
1,静态存储期(全局或静态变量):
所有未显式初始化的成员会被自动初始化为零(如整数为0,指针为NULL等)
如上述代码中,将“,4”删掉,printf("学龄:%d\n",Student1.iyear); 。输出的结果将会为0
2,自动存储期(局部变量):
2.1,未使用任何初始化式:成员值为随机(取决于栈内存的残留数据)。
2.1.1,若结构体变量完全未初始化(自动存储期),成员值是随机的。
例如:
#include <stdio.h>
struct Student{
int iAge;
char cName[64];
int iyear;
};
int main(){
struct Student Student2 ; /*这里Student2是没有赋任何值的*/
printf("姓名:%s\n",Student2.cName);
printf("年龄:%d\n",Student2.iAge);
printf("学龄:%d\n",Student2.iyear);
return 0;
}
结果为:
姓名:
年龄:4199856
学龄:0
2.2,使用了初始化式但未覆盖所有成员(如部分初始化):
2.2.1,若结构体变量有初始化式但未初始化某个成员,该成员值不是随机的,会被置零。
例如:
#include <stdio.h>
struct Student{
int iAge;
char cName[64];
int iyear;
};
int main(){
struct Student Student2 = {2 , "LiHua"};
printf("姓名:%s\n",Student2.cName);
printf("年龄:%d\n",Student2.iAge);
printf("学龄:%d\n",Student2.iyear);
return 0;
}
结果为:
姓名:LiHua
年龄:2
学龄:0
2.2.3,静态存储期的结构体变量即使未初始化,成员值也为零。(如使用static修饰结构体变量)
例如:
#include <stdio.h>
struct Student{
int iAge;
char cName[64];
int iyear;
};
int main(){
static struct Student Student2 ;
printf("姓名:%s\n",Student2.cName);
printf("年龄:%d\n",Student2.iAge);
printf("学龄:%d\n",Student2.iyear);
return 0;
}
输出结果为:
姓名:
年龄:0
学龄:0
2,结构体与数组
2.1,定义结构体数组
其定义方法与定义结构体变量一样,可以这样:
struct Student{
char cName[20];
int iNumber;
char cSex;
int iGrade;
}student[5] ; //定义结构体数组
也可以这样:
struct Student{
char cName[20];
int iNumber;
char cSex;
int iGrade;
};
int main(){
struct Student student[5];
.....
return 0;
}
student[5]表示这个数组可以存5个学生的信息。
此数组中个数据的储存方式如下:
student[0] | cName |
iNumber | |
cSex | |
iGrade | |
student[1] | cName |
iNumber | |
cSex | |
iGrade | |
...... | ...... |
...... | |
...... | |
...... |
2.2,初始化结构体数组
结构体数组的初始化与结构体变量大致相同,不同的是结构体数组初始化时是赋值一个二维数组,而结构体变量是赋值一个一维数组。
如下:
struct Student{
char cName[20];
int iNumber;
char cSex;
int iGrade;
}student[5] = {
{"LiHua",1234560,'M'}
{"ZhangSan",1234561,'M'},
{"LiSi",1234562,'W'},
{"WangEr",1234563,'M'},
{"MaZi",1234564,'M'},
} ;
student[5]中的数字5也可以不填,编译器会根据初始化值列表中的元素个数来确认数组的个数。
练习:输出五猫的基本信息如下:
第 1 只猫:
猫名:糖块 猫龄:1个月 猫的体重:4.900000 猫的性别:M
第 2 只猫:
猫名:小点 猫龄:1个月 猫的体重:3.500000 猫的性别:M
第 3 只猫:
猫名:团团 猫龄:1个月 猫的体重:3.900000 猫的性别:W
第 4 只猫:
猫名:琉球 猫龄:1个月 猫的体重:3.300000 猫的性别:M
第 5 只猫:
猫名:点点 猫龄:1个月 猫的体重:3.700000 猫的性别:W
代码:
#include <stdio.h>
struct Cat
{
char cName[20]; //猫名
int iAge; //年龄
float iWeight; //体重
char cSex; //猫的性别M代表公的,W代表母的
};
int main(){
struct Cat cat[5]={
{"糖块" , 1 , 4.90 , 'M'},
{"小点" , 1 , 3.50 , 'M'},
{"团团" , 1 , 3.90 , 'W'},
{"琉球" , 1 , 3.30 , 'M'},
{"点点" , 1 , 3.70 , 'W'},
};//定义结构体数组,存储猫的基本信息
for (int i = 0; i < 5; i++)
{
printf("第 %d 只猫:\n",i+1);
printf("猫名:%s 猫龄:%d个月 猫的体重:%f 猫的性别:%c\n",cat[i].cName , cat[i].iAge , cat[i].iWeight , cat[i].cSex);
printf("\n");
}
return 0;
}
3,结构体指针
3.1,指向结构体变量的指针
定义结构体指针与定义结构体变量大致相同如下:
结构体类型 *指针名;
使用方法:
#include <stdio.h>
struct Student{
char cName[20];
int iAge;
}student1 = {"李华" , 18};
int main(){
struct Student *p;
p = &student1;
printf("姓名:%s\n",(*p).cName);//p -> cName 与 student.cName ,(*p).cName这三种形式的引用效果是完全等价的
......
}
注意:p -> cName 与 student.cName ,(*p).cName这三种形式的引用效果是完全等价的
使用“ -> ”引用成员时,要注意分析一下情况:
1,p -> iAge; 表示指向的结构体变量中成员iAge的值。
2,p -> iAge++; 表示指向的结构体变量中成员iAge的值,使用后该值加1。
3,++p -> iAge; 表示指向的结构体变量中成员iAge的值加1,计算后再使用。
练习:运用p -> cName 与 student.cName ,(*p).cName这三种形式输出
姓名:张伟
票价:280
乘车区间:成都——上海
车次:D205
乘车时间:2025年02月31日06:50开
代码:
#include <stdio.h>
#include <string.h>
struct Ticket
{
char cName[20];
int iNumber;
char cs[20];
char cAddress[20];
char ctime[20];
}ticket;
int main(){
struct Ticket *p;
p = &ticket;
strcpy(p -> cName,"张伟");
p ->iNumber = 280;
strcpy(ticket.cs,"成都——上海");
strcpy(p -> cAddress,"D205");
strcpy((*p).ctime,"2025年02月31日06:50开");
printf("姓名:%s\n", p -> cName);
printf("票价:%d\n", ticket.iNumber);
printf("乘车区间:%s\n",(*p).cs );
printf("车次:%s\n",p -> cAddress);
printf("乘车时间:%s\n",(*p).ctime);
return 0;
}
3.2,指向结构体数组的指针
1,结构体指针指变量指向结构体数组时,若结构体数组无下标,结构体指针变量的值则是该结构体数组元素的首地址。当然结构体指针也可以直接指向结构体数组中的元素。
struct Student *p;
p = student;
此时输出“ *p ” 则会输出结构体数组 student[0]的值。
2,若结构体指针变量指向结构体数组中的元素时则要这样定义
struct Student *p;
p = &student[2];
输出“ *p ”就等于输出结构体数组student[2]的值。
3,若结构体指针变量指向的结构体数组无下标时,且结构体指针变量p++,(*p)的值则会从student[0]的值变成student[1]的值。
如下所示:
#include <stdio.h>
struct Student
{
char cName[20];
int iAge;
float iWeight;
char cSex;
};
int main(){
struct Student student[5]={
{"张三" , 18 , 62 , 'M'},
{"李四" , 19 , 64 , 'M'},
{"王二" , 17 , 58 , 'W'},
{"麻子" , 18 , 66 , 'M'},
{"李华" , 19 , 53 , 'W'},
};
struct Student *p;
p = student;
for(int i = 0 ; i < 5 ; i ++ , p ++ ){
printf("NO %d 名学生:\n",i+1);
printf("姓名:%s 年龄:%d岁 体重:%f 性别:%c\n",p -> cName , p -> iAge , p -> iWeight , p -> cSex);
printf("\n");
}
return 0;
}
结果:
NO 1 名学生:
姓名:张三 年龄:18岁 体重:62.000000 性别:M
NO 2 名学生:
姓名:李四 年龄:19岁 体重:64.000000 性别:M
NO 3 名学生:
姓名:王二 年龄:17岁 体重:58.000000 性别:W
NO 4 名学生:
姓名:麻子 年龄:18岁 体重:66.000000 性别:M
NO 5 名学生:
姓名:李华 年龄:19岁 体重:53.000000 性别:W
3.3,结构体作为函数参数
1,使用结构体变量作为函数参数
当结构体变量作为实参时,是采用值传递的方式,将结构体变量所占内存的内容全部顺序的传递给形参,同时形参也得是同类型的结构体变量。
参考代码:
#include <stdio.h>
struct Teacher{
char cName[20];
int iAge;
int iSex;
}teacher = {"葵司" , 25 , 0} ;
/**
* @brief 输出结构体变量成员数据
* @param srt 结构体变量
* @return 无
*/
void dispy(struct Teacher srt){ //将实参teacher占用内存的内容顺序传递到 srt形参中来
printf("姓名:%s\n",srt.cName);
printf("年龄:%d\n",srt.iAge);
printf("性别:%d\n",srt.iSex);
}
int main(){
dispy(teacher); //传入实参teacher
return 0;
}
2,使用指向结构体变量的指针作为函数参数
因为使用结构体变量作为函数参数,在传值过程中会耗费大量的空间和时间。而如果运用的是指向结构体变量的指针来作为函数参数,则传递的是结构体变量的首地址,并没有将变量的副本进行传递,所以与用结构体变量作为函数参数相比会节约出大量时间与空间。
指向结构体变量的指针作为函数参数传递的是结构体变量的地址,如果在函数中改变了结构体变量成员的数据,那么返回主调函数时,结构体变量会发生改变。
参考代码:
#include <stdio.h>
struct Teacher{
char cName[20];
int iAge;
int iSex;
}teacher = {"葵司" , 25 , 0} ;
/**
* @brief 输出结构体变量成员数据,并且改变结构体变量teacher中的成员iAge的值
* @param *srt 结构体指针,传入的形式也只能是指针形式
* @return 无
*/
void change_age(struct Teacher *srt){ //创建一个无返回值的函数,且参数为一个指向结构体变量的指针
printf("姓名:%s\n",srt -> cName);//用指针方式输出结构体变量成员
printf("年龄:%d\n",srt -> iAge);
printf("性别:%d\n",srt -> iSex);
srt -> iAge = 30;//运用指针改变结构体变量成员的值
}
int main(){
struct Teacher *p = &teacher;//定义一个指向结构体变量teacher的指针
change_age(p); //将指针作为参数传递给函数change_age()
printf("\n");
printf("修改后的年龄为:%d\n",p -> iAge);
return 0;
}
4,拓展
4.1,结构体类型函数的使用
结构体还可以作为函数类型来使用,其使用方法是:结构体关键字(struct)+ 结构体名 + 函数名 + (参数1 , 参数2,)。这个函数的返回类型就必须是结构体类型,要么就直接不返回。
示例代码:
#include <stdio.h>
struct Car{
float x;
};
/**
* @brief 将传过来的地址里边的值加2
* @param *p一个结构体指针用于传递地址
* @return 无
*/
struct Car add( struct Car *q){
q ->x +=2;
// return *q; //主函数中没有用到返回值,所以可返回,可不返回,如果需要返回的话,主函数中需要用一个结构体变量来接收此返回值
}
int main(){
struct Car car = {12};
struct Car *p = &car;
struct Car m;
printf("加油前:%.2fL\n",p ->x);
add(p);
printf("加油后:%.2fL\n",p ->x);
add(p);
printf("加油后:%.2fL\n",p ->x);
add(p);
printf("加油后:%.2fL\n",p ->x);
return 0;
}
4.2,typedef关键字的使用
typedef:是对某一数据类型重新定义(也就是重新给它起个名儿)。
比如,我我感觉 double 这个数据类型的关键字太长了,后边的时候还很多,所以我们就可以用关键字:typedef
用法:typedef double d;
这样写后,定义double类型的变量时,只需要:d x; 就定义了一个double类型的变量x了。
下面代码与4.1代码的功能相似:
#include <stdio.h>
typedef struct{
float x;
}Car; //此处的Car并不是结构体的变量,而是将这个结构体类型的名字改为Car
/**
* @brief 将传过来的地址里边的值加2
* @param *p一个结构体指针用于传递地址
* @return 无
*/
Car add(Car *q){
q ->x +=2;
// return *q;
}
int main(){
Car car = {12};
Car *p = &car;
printf("加油前:%.2fL\n",p ->x);
add(p);
printf("加油后:%.2fL\n",p ->x);
add(p);
printf("加油后:%.2fL\n",p ->x);
add(p);
printf("加油后:%.2fL\n",p ->x);
return 0;
}
对比发现,在定义结构体变量或指针时,少了结构体的关键字,直接用一个Car来替代了 :关键字+结构体名。
4.3,嵌套的结构体
结构体中的成员不仅可以是基本类型,还可以是结构体类型的。
例如,输出班上男生、女生的名字。先定义一个男生名字结构体,成员为普通字符数组类型名字,用来存3名男生的名字,然后定义一个班级所有人的结构体,前三个成员为普通字符数组类型来存女生名字,然后在定义男生名字结构体变量。
代码示例:
#include <stdio.h>
typedef struct {
char cName1[20];
char cName2[20];
char cName3[20];
}boy;
typedef struct {
char cName1[20];
char cName2[20];
char cName3[20];
boy number;
}class;
int main(){
class all_number = {"唐磬","王青","彭芸",{"李华","张亮","王浩"}};
printf("女生有:\n");
printf("(1)%s \n",all_number.cName1);
printf("(2)%s \n",all_number.cName2);
printf("(3)%s \n",all_number.cName3);
printf("男生有:\n");
printf("(1)%s \n",all_number.number.cName1);
printf("(2)%s \n",all_number.number.cName2);
printf("(3)%s \n",all_number.number.cName3);
return 0;
}
任务二,实现嵌套两次
例如,输出班上男生与女生的基本信息。先定义一个班结构体,成员为男生、女生,在定义一个男生结构体,成员为每个男生的基本信息(姓名、学号、岁数),最后在定义一个女生结构体成员为每个女生的基本信息(姓名、学号、岁数)。
代码示例:
#include <stdio.h>
#include <string.h>
typedef struct {
char cName[20];
int iAge;
int iSno ;
}info;
typedef struct{
char cClass[20];
info boy[3];
info girl[3];
}class;
int main(){
class no1 = { "初三四班",{
{"李华", 18 , 22032},
{"张亮", 19 , 22034},
{"王浩", 18 , 22036}
},
{
{"唐磬", 17 , 22031},
{"王青", 18 , 22033},
{"彭芸", 18 , 22035}
}
};
printf("班级:%s\n",no1.cClass);
for(int i = 0; i < sizeof(no1.boy) / sizeof(no1.boy[0]); i ++){
printf("姓名:%s 年龄:%d 学号:%d\n",no1.boy[i].cName, no1.boy[i].iAge, no1.boy[i].iSno );
}
for(int i = 0; i < sizeof(no1.girl) / sizeof(no1.girl[0]); i ++){
printf("姓名:%s 年龄:%d 学号:%d\n",no1.girl[i].cName, no1.girl[i].iAge, no1.girl[i].iSno );
}
return 0;
}