> 文档中心 > 【C语言功法手册】第七话· 初始结构体

【C语言功法手册】第七话· 初始结构体


💃🕺kiko小剧场


kiko:各位真的是好久不见,【百炼成神】专栏终于要回归了!

小明:真的是拖欠了好久哦🤯···我都不打算订阅了的说。

kiko:呜呜呜😭,我保证,以后一周至少两更!一周两更!

小明:行叭!那就赶紧开始叭~

 🌟🌟往期必看🌟🌟

【C语言功法手册】第四话· 快乐数组小课堂(可做小游戏)https://kikoking.blog.csdn.net/article/details/124718514【C语言功法手册】第三话 · 循环结构一点就通https://kikoking.blog.csdn.net/article/details/124640357【C语言功法手册】第二话 · 语句与if/switch选择结构https://kikoking.blog.csdn.net/article/details/124616889

目录

💃🕺kiko小剧场

🍺知识点1:基本结构体的概念

🍯1.1 结构体的定义

🥝1.1.1 结构体类型的定义

🥝1.1.2 结构体变量的定义

🍯1.2 结构体的初始化

🍯1.3 结构体变量的访问与引用

🥝1.3.1 结构体变量成员的引用

🥝1.3.2 结构体变量自身的引用

🍺知识点2:结构体与函数

🍯2.1 结构体传参

🍯2.2 结构体作为返回值

🍺知识点3:结构体数组的概念

🍯3.1 结构体数组的定义

🍯3.2 结构体数组的初始化

🍯3.3 结构体数组的引用

🍺知识点1:基本结构体的概念


Q1:什么是结构体?

A1:结构体是一种用户根据需求自己构造的数据类型(用户DIY的类型)。我们在前面学习的字符型、整型、浮点型等基本数据类型都是由C语言系统预先定义好的,可以直接用来声明变量类型。

🍯1.1 结构体的定义


结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同数据类型的变量。下面,我们将数组和结构体的定义进行一个对比:

  • 数组:是一组相同类型元素的集合。
  • 结构体:是一组可以相同也可以不同类型元素的集合。

通过对比我们可以发现结构体包含的元素更为广泛。

Q1:为什么会诞生结构体这种类型呢?

A1:这里我发表一下个人看法,我觉得这也是根据生活需求而生,毕竟我们生活中的“对象”都是具有多种元素特征的,比如一个人,他就可以有名字(字符型)、年龄(整型)、体重(浮点型)等信息组成,这些信息的数据类型各不相同,此时就可以使用一个结构体进行定义。


🥝1.1.1 结构体类型的定义

C语言定义结构体的格式:struct 结构体类型名{    数据类型1 成员名1;    数据类型2 成员名2;    ···    数据类型n 成员名n;};

对于上面的格式,其中struct是关键字,表明现在定义的是一个结构体类型。例如我们可以定义一个学生的结构体Stu,用来记录学生的个人信息:

struct Stu{      char name[20];    //名字    int age;   //年龄    char sex[5];      //性别    char id[20];      //学号};      //分号不能丢

我们当然也可以对结构体进行嵌套定义,比如我们在Stu结构体中再嵌套一个名为date的结构体,此时我们就要显定义date结构体,然后再在Stu结构体中嵌套定义:

struct date{int year;int month;int day;};struct Stu{    char name[20]; int age;   struct date birthday; //成员变量birthday被声明为date结构体类型};  

🥝1.1.2 结构体变量的定义

关于对一个变量进行结构体类型的定义主要有以下三种方法:

1、先定义结构体类型,后定义变量

//先定义结构体类型struct Stu{      char name[20]; int age;char sex[5];   char id[20];      };      //后定义结构体变量struct Stu student1[20]; //定义一个具有20个结构体类型元素的数组struct Stu student2; //定义一个Stu结构体类型的变量student2

2、定义结构体类型的同时定义变量

struct Stu{      char name[20]; int age;char sex[5];   char id[20];      }student1[20], student2; //定义结构体类型的同时定义了两个变量

3、直接定义结构体类型变量

struct {      char name[20]; int age;char sex[5];   char id[20];      }student1[20], student2; //定义结构体类型的同时定义了两个变量

这种定义方式是不指出具体的结构体类型名,直接定义结构体成员和结构体类型的变量。

  ✨✨✨我是分割线✨✨✨

🍯1.2 结构体的初始化


结构体的初始化其实和其他数据类型的初始化操作相同,都是在对其进行定义的同时,对成员变量进行赋值操作。例如在下面操作中,我们定义了s1  s2 两个结构体变量,并对s1进行了初始化操作。

struct stu{char name[20];int age;float score;}s2[20], s1={ "张三",20,95.5f };

当然,我们也可以对嵌套的结构体进行初始化操作,其操作如下:

struct date{int year;int month;int day;};struct Stu{char name[20];int age;struct date birthday;}s1 = { "kiko",18,{2022,10,1} }; //对s1进行了初始化操作int main(){printf("%s %d %d %d %d\n", s1.name, s1.age, s1.birthday.year, s1.birthday.month, s1.birthday.day);}

  ✨✨✨我是分割线✨✨✨

🍯1.3 结构体变量的访问与引用


结构体变量的引用主要有两种,一种是对结构体中的成员变量进行访问与引用,另一种则是对结构体变量整体进行引用,接下来我们通过例题来深入解析这二者。

🥝1.3.1 结构体变量成员的引用

结构体变量成员的引用具体来说也分为两种情况,第一种是纯结构体变量访问成员变量;另一种则是结构体指针访问成员变量,这两种的引用方式各不相同,其特点如下:

1、结构体变量访问成员

结构体变量成员的引用是通过点操作符(.)进行访问的,点操作符接受两个操作数,其语法格式如下:

结构体变量名.成员名

例如我们定义一个student结构体,其中含有成员name和age,此时我们该如何访问student中的成员呢?这时我们就可以使用点操作符进行访问,其形式如下:

访问name变量:    student.name访问age变量:     student.age

这时我们就可以使用点操作符进行访问,具体操作如下:

struct Stu{char name[20];int age;}student={"kiko",18};int main(){printf("%s %d\n", student.name,student.age);}

2、结构体指针访问成员

有时我们得到的不是一个结构体变量,而是一个指向结构体的指针,这时我们就需要使用“->”运算符,其语法格式如下:

结构体变量指针->成员名

我们同样举上面这个例子,只不过这里采用了结构体指针,我们看看该如何操作。

struct Stu{char name[20];int age;};void print(struct Stu* ps){printf("%s %d\n", ps->name, ps->age);     //ps为结构体指针,使用->访问成员变量printf("%s %d\n", (*ps).name, (*ps).age); //*ps为结构体变量,使用.访问成员变量}int main(){struct Stu student = { "kiko",18 };print(&student);}

值得注意的是,此处我们同时使用了两种办法来访问结构体中的成员变量:

  1. 直接用指针ps访问,如 ps->name ,ps->age 
  2. 用结构体变量访问,即采用解引用操作*ps来访问,如 (*ps).name ,(*ps).age


🥝1.3.2 结构体变量自身的引用

C语言规定,同类型的结构体变量之间可以进行赋值运算,我们可以通过一个简单的例子来理解:

int main(){    struct Stu    { char name[20]; int age;    };    struct Stu stu1 = { "kiko",18 }, stu2; //st1与st2都是Stu结构体类型    stu2 = stu1; //因此这二者间可以进行赋值运算    printf("%s %d\n", stu1.name, stu1.age);    printf("%s %d\n", stu2.name, stu2.age);}

由上述输出结果可见,当进行 stu2=stu1; 赋值操作后,系统将按成员对应赋值,将stu1中各成员变量的值依次赋值给 stu2。

这里有几点注意:

  1. 在C语言中,不允许将一个结构体变量作为整体进行输入或输出操作,例如下面的语句都是错误的:
    printf("%s %d\n", stu1);printf("%s %d\n", &stu1);
  2. 可以使用sizeof运算符计算结构体变量所占内存空间;结构体变量所占内存空间的大小等于其各成员所占内存空间之和,其操作如下:
    int main(){    struct Stu    { char name[20]; //占20字节 int age;//占4字节    };    printf("%d", sizeof(struct Stu));}

kiko:讲到这里,我们不妨来训练几道题目,看看我们对于结构体掌握如何呀?

001.如有以下代码:struct student{  int num;  char name[32];  float score;}stu;则下面的叙述不正确的是:( )A.struct 是结构体类型的关键字B.struct student 是用户定义的结构体类型C.num, score 都是结构体成员名D.stu 是用户定义的结构体类型名

A:正确,在C语言中需要自定义类型时,要用到struct关键字。

B:正确,在C语言中,用struct定义的结构体,定义结构体类型变量时,需要用struct student。

C:正确,结构体中的变量名称,称之为结构体的成员。

D:错误,stu是定义的结构体类型变量,不是名称,如果想要让stu为结构体类型名称时,必须在结构体定义时添加   typedef关键字。

002.下面程序要求输出结构体中成员a的数据,以下不能填入横线处的内容是( )#include struct S{   int a;  int b; };int main( ){   struct S a, *p=&a;  a.a = 99;  printf( "%d\n", __________);  return 0;}A.a.aB.*p.aC.p->aD.(*p).a

A:正确,结构体类型变量需要访问其成员时,用 . 运算符。

C:正确,如果是指向结构体变量的指针访问时,需要用 -> 运算符。

D:正确,如果是指向结构体变量的指针访问时,可以先对指针解引用,取到指向的结构体类型的变量,再通过 . 访问,但是要注意优先级,因此本题B错误。

🍺知识点2:结构体与函数


🍯2.1 结构体传参


结构体作为一种数据类型,其可以作为函数参数进行应用。由函数章节的知识我们知道,函数有“传值调用”和“传址调用”两种,因此此处结构体传参也有两种方式。我们可以通过下面的两个函数来进行进一步理解:

#includestruct S{int arr[100];};void print1(struct S tmp) //接收一个结构体变量{int i = 0;for (i = 0; i < 10; i++){printf("%d", tmp.arr[i]);}printf("\n");}void print2(struct S* tmp)//接收一个结构体指针{int i = 0;for (i = 0; i arr[i]);}printf("\n");}int main(){struct S s =  {1,2,3,4,5,6,7,8,9,10} ;print1(s); //传值调用print2(&s); //传址调用return 0;}

在上面这个例子中,我们使用“传值调用”与“传址调用”都能实现打印的效果。但是我们需要明白,“传值调用”这种方式会将结构体实参拷贝一份给形参,如果原结构体过大,那么其还要多拷贝一份数据给形参,参数压栈的的系统开销比较大,所以会导致性能的下降。因此,我们推荐结构体传参时,传结构体的地址!

🍯2.2 结构体作为返回值


通常情况下一个函数只能有一个返回值,但如果函数确实需要带回多个返回值,我们就可以使用【全局变量+结构体】来解决,由于一个结构体类型的数据可以包含多个成员变量,从而可以得到多个返回值!我们可以通过下面一个例子来进行进一步理解:

例题1.设计一个count函数,负责计算输入三个数的总和和平均值,并输出。

由于本题的count函数需要输出两个返回值,即总和与平均值,因此我们需要设置一个全局结构体变量,将这两个值作为结构体的成员变量。

struct num{int all;//总和int avg;//平均值};struct num count(int x, int y, int z){struct num tmp;tmp.all = x + y + z;tmp.avg = tmp.all / 3;return tmp;}int main(){int a, b, c;scanf("%d%d%d", &a, &b, &c);struct num result = count(a, b, c);printf("总和为%d,平均值为%d", result.all, result.avg);}

003.下面程序的输出结果是:( )struct stu{    int num;    char name[10];    int age;};void fun(struct stu *p){printf(“%s\n”,(*p).name);return;}int main(){struct stu students[3] = {{9801,”zhang”,20},{9802,”wang”,19},{9803,”zhao”,18}};    fun(students + 1);return 0;}A.zhangB.zhaoC.wangD.18

在main函数中先定义了一个stu结构体类型的数组students,students指向结构体的起始位置,students+1表示该数组中的第一个元素,因此fun的形参实际指向的是students数组中的第一个元素,故打印的是wang。因此本题选C。

🍺知识点3:结构体数组的概念


Q1:什么是结构体数组?

A1:数组是一组具有相同数据类型变量的有序集合,而结构体数组就是一组相同结构体类型变量的有序集合,该数组的每一个元素都为结构体变量。

🍯3.1 结构体数组的定义


结构体数组的定义与先前提到的三种结构体变量的定义方法完全相同,这边简要进行一下三种方式的罗列:

  1. 先定义结构体类型,后定义结构体数组:
    struct Stu{    char name[20];    int age;}; //先定义结构体类型struct Stu student[10]; //后定义一个具有10个结构体类型元素的数组student
  2. 定义结构体类型的同时,定义结构体数组:
    struct Stu{    char name[20];    int age;} student[10]; //定义结构体类型的同时定义一个具有10个结构体类型元素的数组
  3. 不给出结构体类型名,直接定义结构体数组:
    struct{    char name[20];    int age;} student[10]; //定义结构体类型的同时定义一个具有10个结构体类型元素的数组

结构体数组定义好后,系统就会为其分配对应的内存空间,数组中的各元素在内存中连续存放,每一个结构体元素内部的成员变量也按序存放,其存放顺序如下图所示:

  ✨✨✨我是分割线✨✨✨

🍯3.2 结构体数组的初始化


结构体数组的初始化就是在定义结构体数组的时候,对其中每一个元素进行初始化,我们可以简单地通过下面的两个例子来进行理解。

首先,我们对一个具有2个元素的结构体数组student进行初始化操作如下,该操作中我们在定义结构体类型的同时,定义了元素个数为2的结构体数组student[2],并对其中每一个元素都进行了初始化操作。

struct Stu{char name[20];int age;} student[2] = { {"kiko",18},{"asna",22} };

当然,我们也可以省略数组的长度进行初始化操作,这个时候,系统就会根据初始化数据的多少来确定数组的长度,例如下面的操作中,系统会自动确认结构体数组student的元素个数为1。

struct Stu{char name[20];int age;} student[] = { {"asna",22} };

 ✨✨✨我是分割线✨✨✨

🍯3.3 结构体数组的引用


结构体数组的引用其实本质就是数组的引用,使用“ 下标引用运算符[ ] ”来进行引用操作,其一般的语法格式如下:

结构体数组名[数组下标]

比如我们初始化一个结构体数组后,我们要输出该数组中各个结构体元素的值,那么操作如下:

struct Stu{char name[20];int age;} student[2] = { {"kiko",18},{"asna",22} };int main(){for (int i = 0; i < 2; i++){printf("%s  %d\n", student[i].name, student[i].age);}return 0;}

在上述案例中,我们通过student[ i ]来具体访问一个结构体元素,然后用 " . " 来访问该结构体元素中的成员变量。

004.喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以多少汽水(编程实现)。

在正式完成这道题的编程前,我们可以先画一个图来分析一下,我们首先设置三个变量,分别为【钱】、【买来喝的】和【空瓶】,最终程序的输出取决于【买来喝的】这个变量的各项值累加,而【空瓶】转化为【买来喝的】的算法则是本题最值得讨论的点。

通过反复作图我们发现,其实这之间的算法使用到了除2和模2操作,除2可以得到新汽水的数量,而模2可以得到多出来没有兑换的空瓶数。即假如我手上有3个空瓶,我进行3/2可以得到1瓶新汽水,我进行3%2可以知道我手上原本有1个空瓶没有兑换成功,那么当我把新汽水喝完后,我手上现在一共有3/2+3%2个空瓶。

int main(){int empty, total;scanf("%d", &total);empty = total;while (empty >= 2){total += empty / 2;empty = empty / 2 + empty % 2;}printf("%d", total);}

热点资讯