? ?我们经常使用的的数据类型有很多很多,比如int ,char,float等等,我们可以根据不同大小的数据给他分配不同的数据类型,这相比于MATLAB这种语言单调的数据类型C语言对内存的分配更加合理,C语言严格的数据类型可以极大程度的提高程序的运行速度等等。
? 但是随着我们学习的深入,官方给定我们的数据类型只能定义一些指定的数据,适合单一的变量,但是在我们的生活中,每一个实物肯定不止一个特征,就那工资来说,每一个员工都会有一些固有属性,比如,姓名,具体地址,年龄,工资等等。我们要是使用单一的数据类型对其分别定义。在统计少数几个人的时候这种方法看样子还行,然而实际上一个公司的职员少则几百多则上千,一个一个这样单独定义无疑会让代码变的特别复杂。所以C语言就给我们提供了一个可以自己来定义的结构。(这个结构在我看来本质上就是一种数据类型,和int,char地位差不多,只不过他的大小不是固定的,是根据我们的需求进行改变的)
? 结构体是一个或者多个变量的集合,这些变量是不同的类型,把他们都集合在了一起,这样更方便我们使用一些,就拿上文工资来距离,我们使用结构体就会变的特别简洁。
struct gongzi
{
char name[10];//定义姓名
int age; //定义年龄
int num[10]; //定义号码
}
以后我们在定义关于工资的问题就可以直接使用这个数据类型。
结构体成员的访问:
? ? 语法:结构体名.结构成员
struct gongzi
{
char name[10];//定义姓名
int age; //定义年龄
int num[10]; //定义号码
}
gongzi.name=;//通过结构名.成员来调用
补充:两个不同的结构体可以嵌套,但是一个相同的结构体不能嵌套,至于原因吗,我们在结构体的嵌套里面解答。(小透露一下,同一种结构体嵌套就是无限套娃)。
? ? ? 我们在之前的学习里面数组一般都是数据类型+数组名[]来定义的
//数据类型 数组名[]
int arr[];
char arr[];
float arr[];
? 既然是数据类型+数组名,那我们的结构体也是一种数据类型,自然也就有结构体数组。使用方法和普通数组基本一模一样。
struct gongzi //定义方式一
{
char name[10];//定义姓名
int age; //定义年龄
int num[10]; //定义号码
} gz[5];
struct gongzi //定义方式二
{
char name[10];//定义姓名
int age; //定义年龄
int num[10]; //定义号码
} ;
struct gongzi gz[5];
结构体数组访问内部成员
? ? ? ? ?结构体数组名[N].内部成员
? ? ? 他依旧是一个指针变量所以他在内存里面的大小依旧是四个字节,调用方式和一般的指针基本一致,唯一不同的就是结构体指针调用内部成员有一种新的写法。
假设P是一个指向结构体的指针
? 我们可以用p->结构体成员来引用相应的结构体
他等价于(*p).结构体成员
调用方法一://调用方式一(*p.结构体成员)
调用方法二:调用方式二(p->结构体成员)
? ?我们之前学过函数嵌套,那么在结构体里面自然也会有嵌套结构的产生。在上文我们介绍同名结构体不可以进行正常嵌套,但是异名结构体可以嵌套,这是为什么呢。世间万物都是有限的,这也就决定了嵌套的一个前提是你需要有限个,不能层层套娃。
? ?我们同名结构体就会出现这么一个问题。下面我们来看下面的代码
这里我们定义了一个学生结构体,他的成员包括了年龄,姓名,新别,以及同桌,和班级。安装C语言内存的计算,这里我们先假设这个结构体内部没有同桌这个成员。
我们不难得到他所占的内存大小为4+10+4+4=22字节,现在我们把同桌这个结构体加上。
那么现在这个结构体的大小是多少呢,有些人可能会说了,这不就是之前的22字节+这个同桌结构体的大小吗。问题就出现在了这个同桌结构体的上,这个同桌结构体大小到底是多少呢?,因为他们是同一个结构体,所以在同桌结构体里面依旧是22字节+他的另外一个同桌结构体,这样不断的进行递推,我们会发现我们根本无法定义这个结构体的内存大小,按照逻辑推理他的内存大小应该是无穷大的。(下面这个图片应该会好理解一点)
那么我要是非要同一个结构体嵌套改怎么办呢,C语言也给我们提供了解决办法,即通过结构体指针调用。把他从一个递推结构编程一个并行结构。
(这里使用重定义会报错,后续已经更改了)
那么问题来了,现在结构体的大小是多少呢,自然就是我们一直强调的一个知识点,C语言中指针变量的大小不管是什么数据类型他的大小都是四个字节。这里无限大内存的问题就解决了。
那么我们该如何在一个结构体内部调用第二个结构体呢,这就用到了我们上面讲的通过结构体指针访问内部成员。具体讲述我们通过这段代码来介绍。
我们定义了两个结构体分别是张三和李四,在初始化过程中把结构体成员定义为空指针,这里
lisi.classmate = &zhan;需要特殊说明一下,因为lisi.classmate是指针变量,所以他应该对于zhan结构体的地址名。?lisi.classmate->age这段代码就是标准的通过结构体指针调用结构体成员
(一个注意的点,此时结构体定义里面的结构体指针还是使用struct Student 可能是因为此时的重定义还没有生效吧,笔者能力有限,无法解释)
? ?相比于上述的同名结构体通过指针调用来说,异名结构体调用就会简单许多,下面会用一个实例来介绍异名结构体的调用。(因为不会出现内存上无限的问题)
就比如调查一个人,会分为生活方面和工作方面,我们把生活和工作单独定义一个结构体,在定义一个大的结构体来存放这个人信息。
在C语言与程序设计是使用矩形的定义来介绍此嵌套的
即首先我们定义一个直角坐标系的结构体
struct point
{
int x;
int y;
};
然后我们用一个结构体嵌套来定义一个矩形(即通过两个点来定义一个矩形)
struct rect{
struct point pt1;
struct point pt1;
};
? ? ? 我们正常人一般调用函数都是使用if-else哪里不够哪里凑,(大致思路就是通过if'-else对结构体成员的值进行一个判断,然后进行下一步操作)在数据量小的情况下这是没问题的,但是一旦涉及到大工程就极易形成屎山代码,不容易维护且一旦有了新操作就会需要不断的加if-else.
下面由一个产品显示来介绍结构体调用函数。
在实际应用中一家公司会有不同的屏幕驱动函数,不同的产品就会使用到不同的屏幕,正常流程是读取这个产品的ID,然后进入if else进入判断,然后在执行相应的操作函数。如下所示。
(屏幕驱动函数)
代码逻辑就是判断ID然后执行不同的函数(这里我们假设ID1为使用LCD,这里我们假设ID2为使用LCD2)
void LCD_USE1(void)
{
printf("hello\n");
}
void LCD_USE2(void)
{
printf("你好\n");
}
typedef struct PM //重定义屏幕
{
int ID; //使用ID
int name;
}pm1, * pm2; //重定义
int main()
{
pm1 chanp1,chanp2;
chanp1.ID = 1;
chanp2.ID = 2;
if (chanp1.ID == 1)
{
LCD_USE1();
}
else if (chanp1.ID == 2)
{
LCD_USE2();
}
return 0;
}
这样我们的代码逻辑确实非常简单,但是确定就是太过于繁琐了,不容易维护