00:00
呃,各位同学,我们接着来为大家讲解下一个话题,那下面呢,我们来看一下在使用结构体的时候,我们需要注意哪些事情,也就是说我们在用结构体的时候,有哪些地方是需要同学们注意的。那么第一个呢,要给大家讲一下,就是结构体的所有字段在内存中是连续分布的。就是他在内存里边啊,我们说的他在内存里面是连续分布的,那这个怎么理解呢?就说结构体,它它这个比如说我们这个结构体面有呃五个字段。那么这五个字段他在内存里面呢,它的地址大家可以看到,我这里有个图,大家lawyer,比如说吧,我这里这个结构里面有。四个整数,那么这四个整数你们可以看到它的地址其实是连续分布的。那这个连续分布有什么好处呢?像有一些这个,呃,编程的高手,他可以完全通过地址的加减,比如说我通过地址的这个增加多少个字节,然后呢,就找到下一个数,这种方式来快速的获取某一个字段的值,都是可以做到的。
01:16
同时呢,它这种分布形式呢,也利于我们数据的一种查找,就是速度呢,它会比较快,那么我们来看看是不是这样子的呢,那我们举个例子给大家演示一下,然后再画一个图,好我们来看一看啊,这里面我们现在讲的是结构体的。使用注意事项。使用。注意事项。和细节。好,我们把这个呢给同学们讨论一下。给他一个标题三。给他一个标题三。在这里,好,我们来看看有哪几个细节需要注意呢?第一个就是刚才老师说的第一个细节,就这个问题,好,我举个例子。
02:01
让大家认识一下它,它的这个字段在内存中所谓的连续,连续分布是指的什么意思。打开我们的这个。呃,这个Vs code,然后呢,在这里我们新建一个文件夹,这个文件夹呢,我们取个名字,取个名字叫做structure detail。OK,然后呢,我们新建一个文件main.go。Go,好,我们走,Package。PA对吧,然后组,然后呢,我们import。You got for。然后我们写一个主函数。好,那现在呢,为了看到这个效果呢,我们需要定义两个结构体,第一个结构体呢,我们叫做点point,就是记录一个点的。那这个结构体呢,有这么两个字段,结构体有两个字段,一个字段是X,我们把它写成int,一个字段是Y,也写成int,好吧,这两个然后呢,我们再来定义一个结构体是什么呢?叫做矩形RA,然后呢,它也是一个结构体,这个结构体里面呢,它有两个点,就是说矩形的左上角这个点和右下角这个点来共共同的构成了一个矩形,那我写写了啊,Left。
03:27
Up这个点,然后呢,Right。Down这个点好,这两个呢,它们是point这个点好,这就写完了。大家看到这是个结构体对吧,这个没问题吧,这是个结构体。这个呢,大家可以看到这又是一个结构体,对不对,诶又是一个结构体。没问题。这有两个结构体,那现在呢,我们来看一下,我们定义一个RA这个矩形的这么一个结构体,我们可以看到它的内存里面是怎么分布的啊好,我们来走一个吧,R1。
04:01
这个比较简单,所以说我也就不写注释了,R1走,然后呢,我们先来。创建一个结构体。好,那这里面怎么去做呢?大家看这里有两个点对吧,这两个点呢,它又是一个point这样的结构体,所以说咱们在给他。负值的时候呢,可以这样写,看到没有,比如说一二再来point。三四完事。这是一个结构体。变量,那这个时候呢,我们可以知道这个R1呢,它在它的这个内存里面会有四个整数,对不对,这四个整数呢,我们来看看他在内存里面是怎么分布的。好,我们来看下它的内存怎怎么分布的,看看是不是结构体,说一注意听听听这句话啊,R1有。有什么呢?有四个。有四个整数in的形在什么呢?在内存中,在内存中是连续分布的。
05:05
哎,我们把这个题给大家。搞清楚,那么怎么样来证明它是连续分布的呢?只有一个办法就是打印,打印他的地址,把他的地址打印出来。对吧,好,我们来看看地址是不是这样子的,Print人这块呢,我稍微写宽一点。好,走一个。F吧,这样子好不好,这样子就比较好看了,那现在呢,我们先看第一个R1。re.left up。Up,它不是有个X吗的地址。把他的地址打印出来。好,其他意思类推了。我这就快速的拷贝一下re live的Y这个值,它的地址。好把这个D去掉,然后呢,还有就是另外一个点就是。哪个点啊,Write down是不是我们也把它输出来,大家看一下。
06:01
好的,Right down。一个X地址好,同时呢,它还有一个Y这个地址,对不对,也把它输出来。好Y地值,咱们就把它写成这个,那现在呢,大家看到这一行呢,越来越长了,怎么办呢,我们换一行。我们打一个逗号换行输出,好,这个时候地址怎么输出呢?大家都知道我们要输出一个变量的地址呢,要用取值符号对不对?好,其他以此类推。是不是现在以此类推这个呢,应该来个取值符号,这个换成Y再来。这个地方咱们把它改成这个right down。OK,这边呢,我们也是X再来。R1。他的还有一个right。Wid down对不对?是什么呢?Y,好,这多写了一个T,好同学,我们来,我们来运行一下,我们来看一看re的这四个点是不是在内存里面是连续分布的,来运行一下。
07:11
呃,我们进入到。那个chapter。啊,我们先进入到SR,我看这个下面有没有啊,找CD chapter什么呀,幺零,这是我们的DRR,我们在CD到哪里呢?Detail。哎,Detail写错了。CD到。诶,Detail都没出来啊,好,这样就可以了,然后呢,我们来go run一下刚才我们写的这个文件,我们注意观察,同学们注意观察。当我们运行过后呢,我们发现了一个非常有意思的现象,大家可以看到在这里。看第一个X的点呢,是这个,第二个是这个,诶我们发现它们是不是刚好相差八。
08:02
是不是相等八,你再再看这一方。诶,这个地方刚好就挡住了啊,这个画的性能不太好,重新来大家看这里,这是340。这个是38刚好相差八个字节,这个呢,相差多少,又相差八个字点,因为这变成五零了嘛,加八它进一为16进制的这边就是五八,看到没有,这说明他们这四个点。的的确确在内存里面是连续分布的,那如果说我们画一个内存图来描述这个东西的话呢,它其实是这样子的,来打开我们的这个。Excel文件,我们来画一个图。把这个图画完了过后呢,大家就彻底的明白了啊,我们就画一次,后面呢,老师就不再多画了,嗯,怎么个意思呢,同学们可以看到。就是说比如我们就以这个为例。就以这个为例,其中的这段代码,我们把它先拷贝过来。
09:01
OK。好,把它放好,那假设我们的代码执行到了这句话。哪一句话呢?执行到re等于rere什么什么东西好,在内存里面它是怎么分布的呢?同学们可以看到在内存里面它是这样分布的。在内存里面它是这样分布的,OK,这样分布啊,首先呢,它这里面会有一个R1这个变量,这个是不说的,肯定有个re变量对吧。好放这然后呢,它直接指向了一个结构体,对不对,它指向一个结构体,好,我们画一个结构体出来。Okay。好画一个结构体,它这个结构体呢,我给它标个黑色,它这里面有几个元素,有几个字段呢?有四个字段,一个字段。两个字段。三个字段好可以了,那这块因为我画的比较大啊。所以我这边稍微挪一挪。
10:01
走往这边再挪一挪,好,那这里面我们一共有几个点呢?有四个点,第一个点。第二个点。第三个点,第四个点,因为我的值刚好就是1234。对吧,那这样子布局了以后呢,同学们可以看到他在内存里面的这个地址,的的确确,我们可以看到第一个点。就是X这个点呢,它的地址是这样子的,对吧,是一个四零。来,咱们把它放进去。欧了。同学们可以看到,诶,这就是第一个点的位置。对吧,那第二个点的位置呢,来往这边挪一挪。第二个点,第二个点的他的地址其实就是在这个基础上呢,怎么样加了八个字节,那我问大家一个问题,为什么是加了八个字节,而不是。而不是六个字节或者五个字节呢,是不是就是因为它的数据类型刚好是int类型呢,一个INT64的,它的就是八个字节,它默认是INT64的类型,好同样的道理,在这个位置第三个点。
11:09
第三个点的地址呢,又在这个基础上加了一个八,那就是变成五零了,同样第四个点呢,同学们第四个点的位置,诶,它又加了八个,那同学们注意这个特点啊,我们发现它的确是按照这个方式连续分布的,也就是说它的结构体里面的字段的数据呢,它是在内存里面是连续分布,那这个连续分布有什么好处?有什么好处?第一个好处就是说,因为它是连续的,因此它在取这个值的时候,肯定在内存里边底层,它肯定是通过地址,通过地址的加减来找到对应的这个值,因此速度会非常快。对吧,所以说结构体的操作呢,它本身速度就偏快,因为它是按照地址来直接进行加减,比如说我第一个找到第一个第一个字段值,我找第二个字段,他在内存里面怎么做呢?他自己加八个字八,八个字节一下就找到第二个字段的值了,能力眼好,这个特点大家一定要有一个初步印象啊,好,这是第一个案例啊,那么第一个案例说完了以后呢,我们还要再举一个案例。
12:15
举个什么案例呢?就是说嗯,还有一个特点,还有一个特性就是说如果这个react它里面存的不是具体的值,而是指针的话呢。那他这个指针里面的内容就不一定是连续的了。什么意思呢?各位朋友,我再举个例子B,注意听讲啊,那现在呢,假设我们还有一个结构体。它叫REACT2里面呢,它也有。这么两个点,一个是左上角,一个是右下角,好,那现在呢,我们又来创建这么一个R2,注意听,但是呢,我要把它改成什么呢?指针,也就是说此时此刻它里面这个结构体,它存的内容不是一个具体的一个指,而是一个什么呢?而是指针类型的,那这个指针。
13:04
是不是它本身。本身向的内容是个地址啊,就说指针的内容,它指向的内容是个地址,对不对,它内容是个地址,但是指针本身是不是也也有地址啊。那我告诉大家,注意听R有两个什么玩意呢?有两个这样的类型,注意听啊,Point类型。类型。我要,我要下的结论是什么呢?就是这。这两个。Point point类型的地址,他们的地址也是也是连续的。连续的啊,我应该这么说,他们这两个呃,Po指针类型,它们本身的地址啊,本身的地址也是连续的,但是他们指向的地址不一定是连续的,注意听,但是注意听,但是他们指向的。
14:04
他们指向。他们指向的啊。指向的地址不一定不一定是连续的。好,这一点呢,我要给大家做一个证明,我要做一个证明,还是老规矩,还是老规矩,我们呢,来创建这么一个结构体变量,OK,好,既然是R2,那么既然你这个是R2了,那我这个地方地址应该改成什么,就要付的时候是不是分个地址过去啊。是这个意思吧,那么当当然这个值这个point点呢,我把它稍微的改一改,比如说这个改成十。这个改成20,这个改成30,这个改成40,那同学们来,我们仍然呢,也把他的地址打印出来给大家搂一。OK,来这个是不是变成R2了,R的这个X不说了,因为它是一个指针,它肯定不能这样直接取值了,对吧,就是R2.left,它的地址就是说它本身的地址啊,就说这个指它是个指针吧。
15:10
它本身的地址是多少。同样,我们在取R2的。好,因为这是两个指数,我就这样写了啊二二。R2.right这个它的本身的地址。最听讲本身。的地址,好,那因为它只有两个,所以说后面呢,我们就不再打印了,那这个时候我们把它地址取出来,怎么取啊,是不是还是取地字符,但是后面不要X了,同样道理,呃,要把它的。Right down这个地址取出来呢也一样,但这个改成二。来,同学们看一下二。好,我把这个地方也去掉了,没问题吧?同学们好,我们来执行一下。我们来指一下,我们发现,我们发现同学们发现,诶,你们有没有发现他的地址仍然也是连续的。
16:04
这是11C0,这个是1C8,是不是也是八位八个字节呀。为什么是八个字节,因为它的类型其实说白了是int新,它底层是int新,这这这种类,它指向是一个特新的。就它指向是一个呃,Int类型的指针对不对,好,那所以说它也是连续的。它也是连续,但是我要说的是它本身的地址是连续的,但是它它本身这个它的它的内容就是它指向的这个地址不一定。是连续的,当然但也有可能是连续的啊,就就看那看这个实际情况了,这点呢,我就做一个结论啊,就说注意听啊,就是。他们。他们,他们指向的地址。地址。不一定,不一定。
17:02
是连续的。好,这一点呢,我就不去做测试了,你们有兴趣的同学可以这样去做测试,说老师我就想把地址打印出来也可以。这样子。就是如果一一定要把它本身的地址打印出来,呃,它本身它的纸箱的地址打印出来,那就很简单了,这样子。这就他叫子星的弟子。对不对,它指向地址这样写,同样这写它指向的地址。好,那么纸箱的地址是不是就不要这个,不要这个符号了。对不对,是不是不要了,那么我们来跑一下,我说的是不一定是连续的,那也也就是说也有可能是连续的,主要是看内存的情况啊,这个要看什么呢?这个。这个要看,要看这个编译器或者或者系统当时当时是怎么,当时是怎么分布的,当时。啊,在运行时,在运行时是如何如何这个分配的啊,这个他不敢保证是连续的,但也有可能是连续的来走一个我们跑一下。
18:12
好,我们看到他执行的地址。四。零九呃,090,你看同学们发现是不是就完全。看不出来是什么,你看这个4000000090,这个是400000000A0。好像也还能看出来有点规律,为什么呢?因为这个九我在这个基础上如果再加一个,再加一点,加一个值的话,肯定你你看啊,这个九零和A0相差就比较大了。对吧,相差是比较大的,一看肯定不是连续的。你看啊,这是九零,这是A0,你想相差多大了?是不是相差很大呀,他肯定不是相差,简简单单的,比如说八个字节或者16个字节,嗯,不是相差那么一点了,因为你九到A,你想想就相差很大是吧,所以说他这个地方呢,就不知道他是具体它是怎么来分配的了。
19:07
而且每一次可能都不一样,这要根据它的内存来说,因此这个呢,我们就做一个结论,就说它不一定是连续的啊,也有可能是连续的,好了,那对应的这个内存图我们要画一下的话呢,它就是这个图,这个图我就不画了,我直接给他看。好,大致它的内存图就这样子一个情况。就这样子的,指针本身的地址还是连续的,但是指型的地址不一定连续的,比如这个地址,这个地址他们这个这个地方是连续的,但是它指向的这块空间。不好说。哦,这两个地就是他指的这个地址和这个指向的地址不一定是连续的,好,这个结论呢,我们就放到这好吧,来,同学们,我把这个笔记给大家补一补。好,我先把这道把这个练习题。把这个题给大家。把这段代码给大家板述一下对不对?这是我们第23行。
20:02
没问题,我先把它整理到这里。OK啊。好,紧接着呢,我们接着截取第24行。下面的内容。好,这块呢,我们也把它进行一个板书。好,没问题,放好了。没吧,很简单,那这个图这个就是上面这段代码对应的什么呢?它对应的对应的这个分析图呢,我也给同学们放到这边好吧。对应的图,好这呢,我就分析了第一个第一段代码的内存图,但是大家也能看的出来是这么一个意思,对吧,大家从这可以看出来,它的的确确是连续的,看这。对吧,到这儿来,这儿到这儿来,这儿到这儿来,每次相差几个字节呢?八个字节。八个字节,这边也是相差八个字节诶。怎么回事啊,掉了八个字节,这又形成八个字节,因此是连续的。
21:03
那我把它进一个本书。好同学们,那第一个关于我们结构体的第一个细节呢,就给大家先介绍到这里,这个因为写的代码比较多,所以说呢,老师第一次讲的是比较仔细的,因为这个地方啊,也经常是作为一个面试题,或者说在用的时候呢,也也经常会用到这点,因为你看有些为什么我要强调这一点啊,就是因为有一些这个计算机啊,学的就是说他编程编的比较熟的人,他可以直接通过地址的加减来取得这个这个这个我们结构体里面的值。我记得以前我工作的时候啊,第一次我们刚刚参加工作的时候呢,我们做的是一个在Linux下面做C开发,做C开发的时候呢,我们做了一个服务器,这个服务器当时呢,有一个有一个功能就是要去获取到用户在线的状态。当时我因为刚刚参加工作嘛,没有经验,那我怎么做的呢?我做的非常简单,我就直接把每个用户的信息,那那个状态啊状态直接存在MYSQ数据表里面去,结果我把这个设计方案拿出来的时候呢,我们当时的这个头,他也是新浪的第一任第一任CTO,他就狠狠的批评了我,他说你这小伙子这样写是不对的,如果你把所有的信息都往这个数据库里面去存,那么获取这个用户的状态,这个压力会非常大,因为用户的状态呢,变化非常频繁。
22:31
对不对,他一会上线一会离线,那如果说你放到数据表里边去,大家想一想,你的数据库操作会非常频繁,因此这个承压能力是很低的,他又想了一个办法,因为当时我们用的是C语言。他就想了一个办法,他把这个用户的状态直接放在一个结构体里边的。啊,然后呢,你有一个用户呃来了,我就把一个用户的ID和他状态直接放进去,等到我要查找某一个用户信息的时候,他直接通过这个结构体的地址进行一个加,比如说我要根据这个用户的编号,然后呢,我迅速的定位它的这个地址,然后把这状态取出来,好,只要只做了这么一点点改动啊,整个这个性能提升是非常非常大的,所以那个时候呢,我就发现,就说有经验的程序员和刚刚入行的程序员,他们写代码质量真的是有差别,这点呢,我希望同学们就是在写代码的时候多动脑筋,好就就我们就把这个原理告诉大家,为什么可以这样做,因为他们在内存里面是连续的,能理解啊。好的,那第一个细节呢,我们先给大家介绍到这里。
我来说两句