写在前面: 本系列内容均为自学内容,旨在补一下C语言知识,以便更好的学习STM32单片机,笔者没有系统的看过相关教学视频,所以不会有太多的理论解释,并且可能会存在很多问题,如果有大佬路过发现了,还望帮忙指正,感谢各位。
以下内容较为简单,不会像其他大佬那样去总结那么多的内容,因为笔者本人也是个初学者,以下内容是笔者在看了一些简单的资料后的一些心得,旨在简单了解结构体;如果想要更加全面的去了解该篇内容可以在网站搜索其他大佬的内容,有更加详细的文章讲解该篇知识点。
学习目标
1、简单了解结构体的概念,了解结构体的定义、声明、初始化等;
2、结构体与数组;
3、结构体与指针;
一、结构体的定义、声明、初始化等
1、结构体的概念
结构体可以理解为一种用户自己定义的数据类型,不过这个类型中存在多个成员,这有点类似于数组,不过数组中的成员都是相同的类型,结构体中的成员可以是不同的类型,例如我们可以将整型和字符型都定义在同一个结构体中,当然这些成员某种意义上说还是存在这一定的关联的,并且我们可以对这些成员进行赋值等操作;
2、结构体的声明、定义和初始化
结构体在使用前需要先进行声明,声明时需要用到关键字struct
,定义一个结构体时需要给这个结构体起一个名字,这里暂时用tag
代替,至此我们就可以往结构体中加入成员了,这里用list
表示结构体的组成员列表,于是有了以下定义声明:
struct tag
{
list;
};
上述定义中list可以表示int a;
,也可以表示char b;
,值得注意的是,每个成员后必须加上分号,定义完结构体后也要在结构体后加上分号;
定义好结构体后并不能直接使用结构体,还需要针对结构体定义一个结构体变量,同时可以对这个结构体变量或者说这个结构体进行初始化,具体如下:
struct tag ex1={"lihua",'f',1};
其中需要注意的是struct tag
表示结构体类型,而后面的ex1
则是定义的结构体变量,等号后面的内容就是对结构体变量的初始化;
当然在定义结构体时还可以使用以下方式:
struct
{
list;
}tag;
不过这种方式就不需要再次定义结构体变量了,这个定义中的tag
就是结构体变量;
当然我们在定义结构体时还可以加上关键字typedef
,例如:
typedef struct
{
list;
}tag;
此时的tag
就还是结构体的名称,这是在定义结构体变量时就可以按照下面的方式定义:
tag ex1;
这里的ex1
就是结构体变量;
在此复习一下前两节中提到的格式符号,本文仅列举在本文中使用到的符号:
表3-1:部分格式符号
符号 | 含义 |
---|---|
%d | 输出一个整型数据,按其实际长度输出 |
%s | 输出一个字符串 |
%c | 输出单个字符 |
3、结构体小实验
我们可以按照上述内容做一个小实验,程序如下:
程序3-1:
#include "stdio.h"
struct student//定义一个名为student的结构体
{
char name[20];
char sex;
int num;
};
int main()
{
struct student stu = { "lihua",'f',01 };//定义一个结构体变量,并初始化
printf("%s\n", stu.name);//按照次序一次输出结构体中的成员
printf("%c\n", stu.sex);
printf("%d\n", stu.num);
//return 0;
}
在上述程序中,首先定义了一个结构体,并在结构体中列举了三个成员,紧接着在主函数中定义一个结构体变量以便操作结构体中的成员,同时对结构体进行初始化,此时即可将结构体中的成员进行输出,而输出的数据如下图所示:
图3-1:
4、结构体嵌套
结构体嵌套,顾名思义就是在一个结构体中定义成员时使用结构体类型进行定义,具体如下:
struct t1
{
int a;
int b;
};
struct t2
{
int c;
char d;
struct t1 p;
};
上述中的p就是结构体t1类型的变量,同时又是t2的一个成员,于是我们可以做以下操作:
程序3-2:
#include "stdio.h"
struct number
{
int a;
int b;
};
struct student
{
char name;
char sex;
//int num;
struct number num;
};
int main()
{
struct student stu;
stu.num.a = 1;
stu.num.b = 2;
printf("%d\n", stu.num.a);
printf("%d\n", stu.num.b);
printf("\n\n");
//return 0;
}
运行后的结果如下:
图3-2:
上述操作是通过结构体student
输出结构体变量num
,由于结构体变量来自结构体number
,而这个结构体中有两个成员,所以我们就可以对这两个成员分别赋值并输出;
二、结构体与数组
据说单个结构体变量在实际使用中作用不大,反而是以结构体类型的数组的形式出现,所以尝试实现下面的效果,直接看程序,程序中第一行是该程序需要实现的效果,该程序下方放了一个表格,这个函数输入的内容就是表格中的内容:
程序3-3:
//目标:输入4个学生的姓名、学号、3科成绩,并算出平均分,同时算出单科平均分,包括学生成绩平均分的平均分(这道题来自网络,下列程序如原题答案略微不同,具体在程序中标注了)
//下列程序对部分地方进行了标注,旨在方便理解
#include "stdio.h"
#include "stdlib.h"
//定义结构体类型
struct student
{
char name[20]; //姓名
long number; //学号
float record[4];//成绩,存放3科成绩及平均分
};
//主函数
int main()
{
void input(); //函数声明
void aver();
void order();
void output();
void outrow();
struct student stu[4];//共4个人
//float row[3]; //这个题的源程序中有这句话,不过实验时发现屏蔽掉也能运行
input(stu,4); //调用函数
aver(stu, 4);
order(stu, 4);
output(stu, 4);
outrow(stu, 4);
}
//输入函数
void input(form, n) //输入结构体类型form的n个元素,n个学生
struct student form[]; //定义形参
int n;
{
int i, j;
char temp[30];//用来存放输入的数据,长度长一点应该也是没关系的,够用就行
for (i = 0; i < n; i++)
{
printf("\ninput Name,Number,Chinese,English,Math\n");//没啥用,可以不写
gets(form[i].name);//gets函数是输入函数,类似于scanf
gets(temp);
form[i].number = atol(temp);//atol:强制转换为长整型
for (j = 0; j < 3; j++)
{
gets(temp);
//这个题的源程序中下面那句话是转换成整型了,我这里转换成浮点型了,因为结构体中定义的浮点型
form[i].record[j] = atof(temp); //atof:强制转换为浮点型
}//; //这个题的源程序中这个地方有一个分号,这里去掉了,不过去掉好像也不影响
}
}
//计算每个学生的成绩平均值
void aver(form, n)//n个学生
struct student form[]; //定义形参
int n;
{
int i, j;
for (i = 0; i < n; i++)
{
form[i].record[3] = 0;//先将存放平均数的位清0
for (j = 0; j < 3; j++)//计算3科总成绩
form[i].record[3] = form[i].record[3] + form[i].record[j];
form[i].record[3] = form[i].record[3] / 3;//除3算平均分
}
}
//排序,冒泡排序
void order(form, n)//n个学生
struct student form[]; //定义形参
int n;
{
struct student temp;//用于存放数据,两数组数据交换时的中间参数
int i, j;
for (i = 0; i < n - 1; i++)//表示要对比的总次数
for (j = 0; j < n - 1 - i; j++) //还剩多少次
{
if (form[j].record[3] > form[j + 1].record[3])
{
temp = form[j];
form[j] = form[j + 1];
form[j + 1] = temp;
}
}
}
//输出
void output(form, n)
struct student form[];
int n;
{
int i, j;
printf("************************TABLE************************\n");
printf("-----------------------------------------------------\n");
printf("|%10s|%8s|%7s|%7s|%7s|%7s|\n", "Name", "Number", "Chinese", "English", "Math", "Average");//格式符号前的数字表示长度,注意输入时不要超过长度限制
printf("-----------------------------------------------------\n");
for (i = 0; i < n; i++)
{
printf("|%10s|%8ld|", form[i].name, form[i].number);
for (j = 0; j < 4; j++) //这里写4不写3是因为包含了平均分
printf("%7.2f|", form[i].record[j]);//7.2:表示长度7,保留两位小数
printf("\n");
printf("-----------------------------------------------------\n");
}
}
//计算单科平均分
void outrow(form, n)//n个学生
struct student form[];
int n;
{
int i, j;
float row[4] = { 0,0,0,0 };//存放学科平均数数据,加上平均数的平均数共可以计算4个
for (i = 0; i < 4; i++)//表示学科数
{
for (j = 0; j < n; j++)//表示学生数
row[i] = row[i] + form[j].record[i];
row[i] = row[i] / n;//这个两个for循环就是计算用的
}
printf("|%19s|", "Average");
for (i = 0; i < 4; i++)//输出计算后的结果
printf("%7.2f|", row[i]);
printf("\n-----------------------------------------------------\n");
}
上述程序中包含输入函数、求每个学生的平均成绩的函数、根据平均学生的平均成绩排序的函数、求每个学科的平均成绩的函数(这个函数中把学生的平均成绩也求了平均)以及输出函数,而主函数中之需要定义变量然后调用即可;程序中对部分程序进行了标记,这里就不再解释了,直接看以下最终运行结果,以下方表格为输入的数据:
表3-2:4名学生的3科成绩表
姓名 | 学号 | 语文 | 英语 | 数学 |
---|---|---|---|---|
李华 | 1 | 98 | 95 | 97 |
李梅 | 2 | 96 | 95 | 92 |
李雷 | 3 | 94 | 92 | 96 |
李东 | 4 | 97 | 93 | 98 |
图3-3:运行结果
上面的图片即为最终运行结果,分4次输入完学生数据,最后得到表格;
三、结构体与指针
1、结构体类型变量的指针变量
简单来说就是在单个结构体变量中使用指针形式表达,即原来结构体用类似p.name
的形式,而用了指针就是p->name
的形式,当然这里还需要用到一个函数malloc
,这个函数在使用时可以使用下列的方式:
char *p;
p=(char*)malloc(sizeof(char));
通过上面的函数就可以给指针p分配充足的空间供指针使用,貌似也可以写成p=(char*)malloc(100);
的形式,不过这种就是括号中写的数字是多少,那分配的空间就是多少,这种貌似容易导致空间不够用;这个函数在使用时最好搭配函数free()
一起使用,以便能够及时释放掉被分配出去的空间,下面看一下程序:
程序3-4
#include "stdio.h"
#include "stdlib.h"
struct data
{
int year,month,day;
};
struct student
{
char name[20];
long number;
struct data birthday;
};
int main()
{
struct student *p1;
p1 = (struct student*)malloc(sizeof(struct student)); //给指针分配空间,长度为结构体类型的长度
printf("input name number birthday\n");
scanf("%s",p1->name); //软件vc中最好用gets
scanf("%ld",&p1->number);
scanf("%d%d%d",&p1->birthday.year,&p1->birthday.month,&p1->birthday.day);
printf("\noutput name number birthday\n");
printf("%s,%ld,%d-%d-%d\n", p1->name, p1->number, p1->birthday.year, p1->birthday.month, p1->birthday.day);
free(p1); //将分配给指针的空间释放掉,也就是说收回分配出去的空间
//return 0;
}
最终运行结果如下,第二行是手动输入的内容,最后一行是输出结果:
图3-4
2、结构体变量数组的指针变量
这个与前面的类似,不过这次是针对数组的操作,本次针对一维数组进行操作,直接看程序:
程序3-5
#include "stdio.h"
#include "stdlib.h"
struct data
{
int year,month,day;
};
struct student
{
char name[20];
long number;
struct data birthday;
};
int main()
{
int i;
struct student* p1, stu[3] = { {"lihua",1,2001,1,25},{"limei",2,2001,5,30},{"lilei",3,2001,4,18}};
p1 = stu; //将数组stu的地址赋值给p1,使得p1指向数组
printf("output name number birthday\n");
for(i=0;i<3;i++)
printf("%s,%ld,%d-%d-%d\n", (p1+i)->name, (p1 + i)->number, (p1 + i)->birthday.year, (p1 + i)->birthday.month, (p1 + i)->birthday.day);
//return 0;
}
最终运行结果:
图3-5
本节的内容就先告一段落,本来后面还有链表、共同体等内容,奈何笔者刚开始学,还不大会,所以剩下的内容会总结到《结构体(二)》中,到时总结好后会发布。