一、了解C语言的数据类型分为几类
敲黑板!来直接上图:
其中的结构体类型(struct)就是属于C语言四大数据类型之一的构造类型
那么结构体是什么呢?
(1)用户自己建立由不同类型数据组成的组合型的数据结构,它称为结构体。
(2)例如,一个学生的学号、姓名、性别、年龄、成绩、家庭地址等项,是属于同一个学生的,因此组成一个组合数据,如student_1的变量,反映它们之间的内在联系。
二、结构体类型的定义与初始化
1.第一种结构体类型定义和初始化方式(先定义结构体类型,再给结构体类型里面的成员变量赋值)
struct 结构体类型名 { 数据类型 成员名; 数据类型 成员名; ... };
注意:大家一定一定一定要注意最后有一个分号(;)
结构体可以理解为是许多其他数据类型的集合体,由于它里面的成员变量可以各不相同,所以它的存储内容十分丰富。例如:设计一个学生,学生里面有学号、姓名、性别、年龄、成绩、家庭地址等项。
struct student { int num; //学号 char name[20]; //姓名 char sex; //性别 int age; //年龄 float score;// 成绩 char addr[30];//地址 };
由我们设计一个struct student结构体类型,里面由int、char[]、char、age和float类型,请注意!struct student是一个整体,不能少写了struct!!!
如下的定义:初始化十分不方便(.操作符的作用:也叫结构体成员变量操作符,用于让结构体类型变量读取里面的成员变量的值)
//对结构体类型struct student进行定义和初始化 //记住别漏了struct!!! struct Student stu1; //初始化如下: stu1.num = 1; stu1.score = 20.5;
2.第二种结构体类型定义和初始化方式(定义结构体类型的同时定义结构体类型变量)
在定义结构体类型后,在第二个花括号后面直接给个变量名,然后再用分号(;)结束,定义如下:
struct Student{ int num; //学号 char name[20]; //姓名 char sex; //性别 int age; //年龄 float score;// 成绩 char addr[30];//地址 }stu2;//stu2为结构体类型(struct Student)的变量名
赋值的操作与第一种初始化方式一样,得挨个赋值
3.第三种结构体类型定义和初始化方式(在第一种定义的同时,直接赋值)
struct Student stu2 = { 1,"李四",'男',12,90.0,"XXXX" };花括号里面的值是挨个进行赋值,如果遇到非初始化,就直接赋值0
struct Student { int num; //学号 char name[20]; //姓名 char sex; //性别 int age; //年龄 float score;// 成绩 char addr[30];//地址 }; struct Student stu2 = { 1,"李四",'男',12,90.0,"XXXX" };
4.第四种结构体类型定义和初始化方式(在第二种定义的同时,直接赋值)
struct Student { int num; //学号 char name[20]; //姓名 char sex; //性别 int age; //年龄 float score;// 成绩 char addr[30];//地址 }stu2 = { 1,"李四",'男',12,90.0,"XXXX" };
5.第五种结构体类型定义和初始化方式(定义无名结构体类型变量)
注意:这种定义方式,就意味着无法继续给该结构体类型定义变量,因为结构体类型名都没有起!!!(初始化与第一种方式一样)
struct { int num; //学号 char name[20]; //姓名 char sex; //性别 int age; //年龄 float score;// 成绩 char addr[30];//地址 }stu2;
结构体成员也可以属于另一个结构体类型。
struct date { int month; int day; int year; }; struct student { int num; char name[20]; char sex; int age; struct date birthday; //此数据类型是其它的结构体类型 char addr[30]; };
三、结构体成员变量的调用
使用成员访问运算符来访问结构体变量的各成员,成员访问运算符用英文的点字符“.”来表示。也有人将其形象地称为点运算符,功能就是通过成员访问运算符来访问结构体变量名所表示的结构体变量的指定成员。
如:结构体变量名.成员变量名(stu1.num)
代码如下:
四、结构体类型起别名(typedef)
首先我们知道了为数据类型起别名可以用关键字typedef
例如:typedef int A; int a;和 A a;的定义结果是相同的。
结构体类型起别名有两种方法:有结构体类型名和无结构体类型名
注意:下面两张图片的代码中的stu就不是结构体变量名,而是结构体类型别名!!!请记住有typedef就没有结构体变量名的存在了!!!
1.有结构体类型名的代码
2.无结构体类型名的代码
五、结构体数组
理解结构体数组,我们可以从整形的二维数组的角度去理解。
首先让我们来回一下二维数组的定义:int a[2][2] = {{1,2},{3,4}};其中就是两行两列的二维数组
那么结构体数组又该怎么定义呢,有两种定义方式:分别是自动匹配和自主给花括号。
1.自动分配定义结构体数组
2.自主给花括号定义结构体数组
六、结构体指针
指向结构体对象的指针变量既可以指向结构体变量,也可以用来指向结构体数组中的元素。但是,指针变量的基类型必须与结构体变量的类型相同。例如:struct student *pt;
例题: 通过指向结构体变量的指针变量输出结构体变量中成员的信息。
解题思路:在已有的基础上,本题要解决两个问题: 怎样对结构体变量成员赋值; 怎样通过指向结构体变量的指针访问结构体变量中成员。
说明: 为了使用方便和直观,允许把(*p).num用p->num来代替 (*p).name等价于p->name 如果p指向一个结构体变量stu,以下等价: ① stu.成员名(如stu.num) ② (*p).成员名(如(*p).num) p->成员名(如p->num)
代码如下:
七、用结构体变量和结构体指针变量作函数的参数
将一个结构体变量的值传递给另一个函数,有3个方法。
(1) 用结构体变量的成员作参数。 例如,用stu[1].num或stu[2].name作函数实参,将实参值传给形参。 用法和用普通变量作实参是一样的,属于“值传递”方式。 应当注意实参与形参的类型保持一致。
(2) 用结构体变量作实参。 用结构体变量作实参时,将结构体变量所占的内存单元的内容全部按顺序传递给形参,形参也必须是同类型的结构体变量 在函数调用期间形参也要占用内存单元。这种传递方式在空间和时间上开销较大 在被调用函数期间改变形参(也是结构体变量)的值,不能返回主调函数 一般较少用这种方法
(3)用指向结构体变量(或数组元素)的指针作实参,将结构体变量(或数组元素)的地址传给形参。
例题: 有N个结构体变量stu,内含学生学号、姓名和3门课程的成绩。要求输出平均成绩最高的学生的信息(包括学号、姓名、3门课程成绩和平均成绩)。
解题思路:按照功能函数化的思想,分别用3个函数来实现不同的功能: 用input函数输入数据和求各学生平均成绩 用max函数找平均成绩最高的学生 用print函数输出成绩最高学生的信息 在主函数中先后调用这3个函数,用指向结构体变量的指针作实参。最后得到结果。 本程序假设N=3
观察下列代码,觉得print1函数好呢,还是print2好些呢?
struct S { int data[1000]; int num; }; struct S s = {{1,2,3,4}, 1000}; //结构体传参 void print1(struct S s) { printf("%d\n", s.num); } //结构体地址传参 void print2(struct S* ps) { printf("%d\n", ps->num); } int main() { print1(s); //传结构体 print2(&s); //传地址 return 0; }
答案是:首选print2函数
原因:函数传参的时候,参数是需要压栈的。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。
结论:结构体传参的时候,要传结构体的地址。