本章概述
- 结构体类型的声明
- 结构体变量的创建和初始化
- 结构体成员访问操作符
- 彩蛋时刻!!!
结构体类型的声明
咱们在讲操作符那个章节中,对于结构体类型的声明进行了讲解,咱们先来回忆一下,为后面的讲解作准备。
- 为什么要有结构体类型? 前面,咱们讲过,变量类型分为内置类型和自定义类型。
char , float ,int
等这些为内置类型,也就是说这些内置的类型是C语言给你规定好的,直接用就OK了。数组,结构体,联合体和枚举
这些就是自定义类型,需要我们自己来定义,定义后才能使用。当我们描述简单的变量时候,一般用内置类型(单一变量类型)就OK了,比如,定义一个整形变量,int i=0; 但是,当我们描述一个复杂的对像时,单一变量就不行了,比如,描述一个学生。描述一个学生需要描述名字,身高,体重和学号。这些描述中有字符类型,整形类型和浮点类型。而我们的单一变量类型就不满足了,需要定义结构体类型来进行描述。 - 结构体的定义:结构体是一些值的集合,这些值被称为成员变量,这些成员变量可以是不同类型的变量。进行结构体的结构展示:
struct tag
{
member-list ; //成员列表
}varible-list; // 定义的结构体变量
可以在创建结构体的时候定义变量,这个时候的变量为全局变量。也可以先创建好结构体,在后面使用的时候,再创建对应的结构体变量,就是局部变量。进行代码展示:
struct stu
{
int age;
char name[20];
double hight;
}s1,s2; // s1 ,s2为全局变量
s1 ,s2
为全局变量 。。
struct stu
{
int age;
char name[20];
double hight;
};
int main()
{
struct stu s1; //s1为局部变量
return 0;
}
s1
为局部变量。
结构体变量的创建和初始化
- 结构体的创建:我们已经在上面写过了结构体的创建格式。我们再来创建一个描述学生的结构体类型吧。
描述一个学生,名字,年龄,身高,学号和体重
struct stu
{
char name[20];
int age;
double hight;
char ID[40];
double weight;
};
- 结构体的初始化:在这里我们要分为两种情况,一种是初始化一部分(不正经的写法),另一种就是全部初始化。我们直接创建局部变量。
- 初始化一部分(不正经的写法):进行代码展示;
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct stu
{
char name[20];
int age;
double hight;
char ID[40];
double weight;
};
int main()
{
struct stu s1 = {"zhangsan",18};
return 0;
}
结果运行图:
我们创建的结构体类型中,有很多的成员,但是我们只初始化了几个成员,而且也没有报错。这样初始化是OK的,对于其它没初始化的成员空间,就会自动初始化为0。调试图所示:
但是,这是不正经的写法。我们既然创建了一个结构体类型,肯定为描述对象,所创建的数据类型(成员变量)刚刚好,我们就是为了全面描述对象的,为什么还初始化不全面呢?对吧。所以,我们要全部初始化,才是正经的写法。
- 初始化全部成员,进行代码展示。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct stu
{
char name[20];
int age;
double hight;
char ID[40];
double weight;
};
int main()
{
struct stu s1 = {"zhangsan",18,180.2,"32301022004",75.3};
return 0;
}
我们还可以不按顺序进行初始化,进行代码展示:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct stu
{
char name[20];
int age;
double hight;
char ID[40];
double weight;
};
int main()
{
struct stu s1 = {"zhangsan",18,180.2,"32301022004",75.3};
struct stu s2 = {.age=18,.ID="32301022004",.hight=180.3,.weight=75.3,.name="zhangsan"};
return 0;
}
- 结构体的特殊声明:一般结构体的声明,咱们都知道,它是有标签名的(tag)。但是,=结构体的声明可以不用写标签名,这种特殊声明就叫:匿名结构体。进行代码展示:
// 匿名结构体
struct
{
char name[20];
int age;
double hight;
char ID[40];
double weight;
}s1; //匿名结构体只能定义全局变量
- 匿名结构体要注意的地方:匿名结构体只能定义全局变量,不能定义局部变量。为什么呢?接下来进行代码讲解:
假如我们用上面的匿名结构体来定义变量:struct s1 。大家是不是看着很不对劲,这是匿名结构体
定义的变量s1 。但是,我们也可以看成 struct s1==struct tag(标签),其中s1就是个标签。
所以,计算机会把struct s1当成正常命名的结构体。所以,为了能够定义匿名结构体变量,我们
只能定义全局变量。
- 结构体使用注意事项:同种类型的同一个结构体可以使用多次,不同种类型的同一个结构体只能使用一次。我们先来写个代码;
写个结构体的类型,我们创建个普通的变量和指针型的变量。我们取出这个普通变量
的地址,存放到结构体类型指针中。
我们先来展示正常的结构体创建的变量代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct str
{
int i;
char arr[20];
}s1;
struct str
{
int i;
char arr[20];
}*p;
int main()
{
p = &s1;
return 0;
}
结果运行图:
结果运行是错误的,在这里我们分开使用了结构体类型,又分别创建了两个不同类型的变量。我们进行&s1
,取出的是s1
的地址,按理来说是可以赋值给p
的。但是,编译器会把这一个结构体类型当成不同的结构体类型,所以就会报错。这就回到咱们开头讲的:同类型的变量可以使用多次,不同类型的变量只能使用一次。进行展示正确的代码:
代码展示【1】:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct str
{
int i;
char arr[20];
}s1,*p;
int main()
{
p = &s1;
return 0;
}
结构运行图:
代码展示【2】:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct str
{
int i;
char arr[20];
}s1;
int main()
{
struct str *p=NULL;
p = &s1;
return 0;
}
结果运行图:
这两种的写法才是正确的,上面展示的两种代码就属于共用同一个类型的结构体。虽然创建的是两种不同类型的变量,但是你没有分开使用,编译器就会认为你使用的是同一个结构体类型,所以不会报错。总结:创建不同类型的变量时,同一个结构体不能分开使用,匿名结构体也是如此。
不是同一个结构体也可以使用(虽然成员列表相同),如下:
- 重命名使用:上面,咱们讲了,同一个结构体定义不同类型变量时,只能使用一次。那么我们有没有什么办法可以用多次呢?——重命名。关于重命名,咱们讲过,
#define
和typedef
。进行代码展示——以typedef
举例:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
typedef struct str
{
int i;
char arr[20];
}node;
int main()
{
node* p = NULL;
node a;
p = &a;
return 0;
}
结果运行图:
当我们重命名后就可以随便用了,匿名结构体也是如此。
- 结构体的自引用:当我们在结构体里面包含一个同一个结构体是否可以呢?进行代码展示:
struct Node
{
int data;
struct Node next;
};
上面的代码,我们定义了一个数据结构里面的链表节点。所谓数据结构就是:数据在内存中的存储方式。我们知道数组就是一种存储数据的方式之一,其实数据在内存中的存储方式很多中。其中有一种不是连续存储的方式就是链表存储。每个链表都有一个节点,这个节点里面存有数据和下一个节点的信息,如图所示:
大家感觉到上面的代码有什么问题吗?我们知道每个数据类型都有大小是吧,那么我来计算一下这个节点的大小。我们发现竟然无法计算。Int data我们清楚占有4个字节,但是 struct Node next占有多少个字节就不清楚了(因为它里面又会包含 struct Node next),就会形成 “套娃” 就会无穷大。我们表示节点示不用这种方式自引用,我们可以存放下一个节点的地址,指针大小咱们知道是固定的。所以节点大小就可以计算了,进行代码展示:
struct Node
{
int data;
struct Node* next;
};
- 重命名的自引用注意事项:我们重命名后,不能在结构体里面自引用这个重命名的结构体。进行代码展示:
typedef struct Node
{
int data;
Node* next;
}Node;
结果运行图:
结果报错了。原因 :因为程序是一步一步在执行的,当执行到 Node* next;
这句代码的时候,编译器在之前没见它就会报错(因为你在后面才重命名的,所以编译器看不到)。进行正确代码展示:
typedef struct Node
{
int data;
struct Node* next;
}Node
这样写就OK了。
结构体成员访问操作符
- 成员访问操作符:有两种访问操作符,
' .'
和' ->'
。其中‘ ->’
只能用于指针形式的访问。' .'
的访问方式:进行代码展示:
#include <stdio.h>
struct stu
{
int i;
char name[30];
};
int main()
{
struct stu s1 = {100,"zhangsan"};
printf("%d %s",s1.i,s1.name);
return 0;
}
结果运行图:
我们还可以直接赋值,进行代码展示:
#include <stdio.h>
struct stu
{
int i;
char name[30];
}s1;
int main()
{
s1.i = 100;
//s1.name = "zhangsan";
printf("%d %s",s1.i,s1.name);
return 0;
}
结果运行图:
我们不能进行点的直接访问数组,原因如下图所示:
' ->'
访问方式:进行代码展示:
#include <stdio.h>
struct stu
{
int i;
char name[30];
};
int main()
{
struct stu s1 = {100,"zhangsan"};
struct stu* p = &s1;
printf("%d %s", p->i,p->name);
return 0;
}
结果运行图:
彩蛋时刻!!!
https://www.bilibili.com/video/BV1MwtteWECz/?spm_id_from=pageDriver&vd_source=7d0d6d43e38f977d947fffdf92c1dfad
每章一句:不要看见别人发光,就觉得自己暗淡
感谢你能看到这里,点赞+关注+收藏+转发是对我最大的鼓励,咱们下期见!!!