zoukankan      html  css  js  c++  java
  • A

    CPU,即Central Processing Unit的缩写,就像计算机的大脑一样。

     假使你了解CPU是如何运转的,你同样也就了解了计算机是如何运转的。

    让我们掀开CPU的顶盖,看看里面正在发生什么

    CPU里面到处都有各种各样携带着信息的引线

    这种叫6502的CPU被广泛运用在苹果,Commodore 64等以及各种娱乐系统上。 

    6502的CPU的模拟器可以在VISUAL6502.org上被找到

    在每一个CPU中,为了让所有部件保持同步,有一条特定的线路被一次又一次地启动,它被称为时钟(Clock),

    在这一次模拟中,时钟电路每秒启动大约两次

    现代的CPU则能达到每秒数百万次乃至数十亿次

    这种高速允许CPU相当快的完成极其复杂的工作

    尽管如此,每个时钟tick里CPU所完成的事情非常的简单

    那是我们稍后会了解到的

    现在,我们离开CPU内部并重新盖上CPU的盖子

    你电脑里的CPU可能是由AMD或者Intel所制造的

    不过我们今天看到的CPU其实都可以叫做Scott CPU

    有一本相关的书籍《BUT How DO It Know?》,你们可以在www.ButHowDoItKnow.com上查阅到相关信息

    这是一本很棒的书,它详细地讲述了CPU的所有知识

    现在我们把CPU翻过来看看下面

    你可以看到一堆从CPU内部延伸出来的引脚,通过他们CPU可以接受或输出数据

    CPU的这些引脚刚好适合我们所熟知的称为主板(MotherBoard)的东西

    主板令计算机的其他部件可以彼此相互连接

    我们把CPU安装进主板

    在主板的右侧安置了内存(RAM)

    RAM是Random Access Memory的简称

    它承载着所有被CPU处理的数据

    现在我们通过研究CPU与内存之间的互动来了解更多关于内存的信息

    现在,我们移除主板的其他部分来研究内存

    内存由许多地址组成,每个地址中都存放着一条数据

    一般情况下CPU按顺序请求读取并处理内存中存放在地址中的数据

     尽管如此,如果CPU打算跳转到其他地址

    它也是可以这样做的

    那就是RAM之所以为随机存储器的原因

    数据可以根据需要被随机选出进行处理

    不过一般来讲CPU处理指令的顺序是一定的

    当计算机开始执行一个程序的时候

    CPU首先向RAM发送一个用于程序开始执行的地址

    RAM中的地址仅仅是有1和0组成的,分别表示线路的高低电平

    RAM啥也不干,直到CPU激活set或者enable线路

    如果enable线路通电了,RAM将该地址中所存储的任何数据通过数据总线传送给CPU

    然后CPU会对数据进行相应的处理

    当CPU完成了它的工作,它传输另一个地址给RAM,并激活enable线路,然后RAM再次返回对应的数据

    在计算机中这样的过程会周而复始

    如果CPU打算往RAM中储存数据

    它首先运输地址,然后运输数据,最后激活set线路

    RAM会用新的数据覆写该地址中的内容

    存在RAM中的内容看上去按完全就是一大堆1和0

    但在这些1和0之间有着小小的不同

    有一些一些比较重要的数据被称为Instruction(指令)

    指令可以让CPU做各种各样的工作

    数据中的另一些部分被称为Number(数字)

    他们被用来做加法,作比较,或者参与其他的一些处理

    内存中还有一些数据是地址

    出于某些特定的用途,这些地址本身也被储存在内存空间中

    这些地址有相当多的用途

    举个例子,如果你想要输出某些东西到外部设备的话,你需要知道该设备在内存中对应的地址

    再举个例子,当你想要输出一些文字到打印机或者显示器

    内存中同样存在一种数据类型为Letter(字母)

    如果你想要在屏幕上展示某些东西,你需要预先在内存中将他们以1和0的形式储存起来

    根据编码集,每个符号都能被不同的1和0的组合所表示

    每个符号的编码都是唯一对应的

    比如这个编码代表a,而这个编码代表G

    这些构成了内存中所储存的绝大部分内容

    现在我们仅仅把它们当作0和1看待

    然后我们把RAM插回到主板上的RAM接口

    把数据列表和地址列表并在一起

    把CPU从主板上拆下

    然后来看看什么是CPU的指令集 

    正如我们先前所知道的,内存中的一些数据被称为指令

    而不同的CPU则有不同的它可以识别的指令集

    它们可能包括一个 LOAD 指令,它可以从RAM中加载一个数字进入CPU

    在 LOAD 指令之后可能是一个 ADD 指令,它可以把两个数字加起来

    在 ADD 指令之后可能是一个 STORE 指令,它可以把计算结果存回RAM中以便日后使用

    指令集中同样可能包括一个 COMPARE 指令,用以比较两个数字的大小关系

    COMPARE 指令在你使用 JUMP IF 指令的时候尤其有用

    就像我们所看到的,CPU从RAM中按一定的顺序执行指令

    有时程序希望CPU跳到另一个指定的地址执行指令

    JUMP IF 就是在跳跃之前检验跳跃条件是否满足的

    它使用 RESOLVE 和 COMPARE 指令进行决策

    同样的,存在一个 JUMP 指令使RAM读取器从一个地址跳往另一个地址

    最后,我们来讲讲 OUT 和 IN 指令

    它们分别有着向显示器等外部设备输出数据和从键盘等外部设备读取数据的功能

    OUT 和 IN 指令在接下来的内容中和地址有着密切的关系

    CPU的指令集中还有其他指令,不过它们不如上述指令常见

    所以正如我们所见,在内存中储存的内容由指令,数字,地址,字母等构成

    现在我们用这些指令来玩一个猜测游戏

    首先CPU会读取一个 9 

    我们的程序需要得知用户是否键入了正确的数字

    所以它需要读取一个数字进入RAM中

    这里来一个 IN 指令处理来自用户的猜测

    在 IN 后面我们放入键盘的地址,这样电脑就知道从哪里读取数据了

    再后面是一个COMPARE指令,它用于比较那两个数字——预先存入的数字和用户键入的数字是否相同

    在COMPARE指令后面是一个 JUMP IF = 指令,用以在上述条件成立的情况下跳到RAM中的另一个地址

    其后是一个地址,如果用户猜对了,CPU跳往这个新的地址以执行对应的指令

    如果那两个数字不一样,计算机会忽略这条JUMP IF 指令和其后的地址,进行下一条指令

    在JUMP IF 和对应的地址后面,我们设置一条OUT指令,然后是输出的外部设备——显示器

    后面我们放入一个大写的G,然后是u,e,s,s,空格,again

    所以是 “Guess again“

    所以如果用户猜错了,我们的程序会输出”Guess again“,然后跳回到 IN 指令以处理用户新的猜测

    然后把整个比较过程再来一遍

    这个程序中的 IN 和 OUT 指令被某种程度上简化了,但你在书里可以看到更多细节

    现在我们来简要地观察观察CPU它自己是如何处理那些指令的

    正如我们前面所说的,这是一块 6502 CPU 的内部 

    我们可以忽略掉来自 6502 的警告然后看看 Scott CPU 的内部究竟是怎样的

    第一个部件是 Control Unit,他就像军队里的领导者一样

    它从RAM和在工作顺序上处于前面的结构接受指令

    然后为其他部件把指令拆解成更具体的命令

    另外一个位于CPU中的重要部件是算术逻辑单元,简称ALU

    ALU是CPU中处理所有数学运算的地方,他可以进行加法、减法,甚至我们之前提到过的比较

    ALU有两个输入端,输入A和输入B,它们分别是之前用 LOAD 指令读入的两个数字

    现在我们想把那两个数字加在一起

    控制单元从RAM接收指令,然后告诉ALU接下来要进行的运算的类型

    ALU进行对应的运算然后输出结果

    有的时候,对于某些特定的运算类型,ALU的输出实际上可以被忽略

    比如,如果要进行的是 COMPARE 指令,ALU并不需要输出答案

    它只需要告诉控制单元比较的结果

    为了实现这个目的,ALU使用我们称之为Flags的东西以指导CU对下一个指令反应,就像 JUMP IF 指令,我们待会会看到的。

    现在我们来研究研究ALU处理需要输出的运算的过程

    那些输出的结果究竟去了哪里呢?

    输出的线路会连接到寄存器(Register)中

    寄存器是一个非常简单的部件,他的功能仅仅是暂时储存数字

    寄存器就像RAM一样,它与RAM的不同点在于它存在于CPU内部,并且可以在CPU正在处理指令时更快更便捷地暂时性存储数值

    当ALU向寄存器输出结果的时候,它并不就此保存结果,除非CU激活了set线路

    这里的 set 线路就像我们之前在谈到RAM时提及的那条 set线路一样

    当set线路被激活,寄存器即保存它从输入线路所接收到的数值

    尽管我们解决了寄存器存储数字的问题,我们还得让数值能从寄存器中被读取

    要把数值从寄存器中弄出来,我们需要一条 enable线路,它同样是从CU引向寄存器的

    CU一把enable线路激活,寄存器就把它所储存的数值输出

    寄存器的输出线路连接到我们称之为CPU总线的地方

    这个总线同样跟我们之前提及的主板上的总线一样

    它仅仅是连接着许多不同计算机部件的一组线

    连接在总线上的寄存器都有自己的set和enable线路

    它们可能已经含有因为先前指令而保存的数值在其中了

    所以CU会激活某个它想要其储存数值的寄存器的set线路

    然后数值就会被保存在这个特定的寄存器中了

    然后CU会关闭最开始的寄存器的enable线路并清空总线

    顶上的四个寄存器只是用来在指令之间储存数值的

    所以它们的输出端和输入端一样都接在总线上

    现在我们通过不断地激活关闭set和enable线路来一个一个地清除这些寄存器中的数值

    同时总线含有一个缺点,这个缺点使得它很容易重置各个部件中的数值

    这个缺点就是:总线在同一时间只能包含一个数值

    因为这个缺陷,ALU设置了一个特殊的临时寄存器用于输入B

    当CU执行某些涉及到ALU的指令时

    它会开启某个寄存器的enable线路使其输入ALU的暂时寄存器中

    暂时寄存器没有设置enable线路的必要

    因为这个寄存器的输出不会使其他寄存器陷入混乱

    ALU的另一个输入端则直接来源于总线

    当CU使一个寄存器输出数值的时候,它会直接成为ALU的输入A

    那个数值会一直存在于总线中直到ALU完成它所执行的指令

    到目前为止ALU的两个输入端都已经准备完毕了

    像我们之前所说的,因为CU从RAM中读取即将执行的指令,它知道ALU将要进行的操作

    指令本身存在于一个称之为指令寄存器的地方

    同时当该寄存器已经储存了指令的时候,总线中的数据变动将不会影响这个寄存器

    指令寄存器同暂时寄存器一样,没有必要单独设一个enable线路

    因为它的输出端直接连向CU

    根据这个结构,CU才得以告诉ALU所要执行的运算类型

    假定要进行的运算是比较

    在比较运算中我们并不关心ALU所输出的结果

    我们只需要知道所比较的两个数字之间的大小关系

    为了达成那个目的,我们使用先前提到过的信号Flags

    每个信号只是一条用激活和关闭分别表示条件是否成立的线路

    在Scott CPU中有4个信号,我们这里只研究两条就够了

    信号“A更大”将会在输入的A比B大时激活

    如果AB是相同的,信号“相等”将会激活

    如果这两条线路都没有被激活,那就意味着输入的B是比较大的

    在下一种情况中,信号“相等”激活了,意味着A和B一样大

    一旦比较指令完成了,我们仍然需要新增一个信号用以将结果保留至下一个指令

    所以我们把信号保存在一个四输入四输出的寄存器中,叫信号寄存器

    当信号寄存器的set线路被激活时

    CPU就完成了他的比较指令,并向RAM请求读取下一条指令

    通常情况下在 COMPARE 指令后面紧跟着的是 JUMP IF 指令

    COMPARE 和 JUMP IF 的指令组合,在计算机编程中是非常常见的

    如果在程序执行的过程中有多条可能的路径,它负责告诉计算机应该选择哪一种

    回到刚才的话题,现在我们已经完成了比较指令并把结果保存在了信号寄存器中

    我们需要告诉RAM我们已经准备好接受下一条数据了——在当前状态下应该是下一条指令

    所以在CPU中我们引入的下一个部件是指令地址寄存器

    CPU通过指令地址寄存器得知RAM中下一条指令的地址

    当CPU已经准备好执行下一条指令时

    它激活指令地址寄存器并把结果输出到总线中

    地址信息会流向RAM,不过当然,不是直接到达那里

    其间会存在一个叫做地址记忆寄存器的中间寄存器

    它唯一的工作就是告诉RAM CPU下一个想查询的地址

    这样CPU就能获得对应的指令了

    当指令地址被植入地址记忆寄存器中时

    它就自动将其输出给RAM,这也是为什么它没有enable线路

    CU紧接着激活RAM的enable线路

    RAM即自动反馈存在于那个地址中的数据,当然在这种情况下是一条指令

    然后指令就再一次存入指令寄存器中

    然后CU再次开始处理

    这时的指令是 JUMP IF Equal 指令,它要求检测 Equal 信号是否被激活

    这两条引线共同组成了一个 与门

    如果两个输入端都被激活了,输出端也相对应的被激活

    输出端被激活会导致触发器跳转

    这会导致RAM向CPU输出下一条数据,这时应该是一条地址

    这条信息就进入了指令地址寄存器中

    当CPU处理完了 JUMP IF Equal 指令后,就开始处理那个地址中储存的新指令

    在那个新地址里的指令可能是向荧幕输出文本“You guess correctly”

    因为我们知道迄今为止用户猜对了 

    最后的四根引线被用以控制外部设备比如显示器和键盘

    我们现在即看到了Scott CPU内部的全景、

    数据在CPU周围的总线中移动

    并根据用途的不同被储存在不同的寄存器中

    我们所见到的每一条指令CPU都可以在6个时钟Ticks内完成

    现代的CPU可以在每个Clock tick内同时处理多条指令

    这意味着你用来看这个视频的计算机可能已经达到了每秒钟处理十亿、百亿、乃至千亿条指令的速度

    只需要其内部的部件越来越先进

    这也使得现代的CPU比Scott CPU复杂许多

    但它们的运行原理其实是一样的

    现在我们到高一点的层级看

    我们就可以发现这些线路都连接着CPU上的引脚

    右边是set和enable线路

    在顶部是与RAM地址有关的线路

    在底部是数据相关线路,其中有连接RAM的也有连接外部设备的

    在左侧是输出输入控制线路

    我们再提高一个层级,完成我们剩下的旅行

    我们把CPU的盖子装上

    然后把CPU安装回主板

    通过左侧的端口,我们可以插上显示器和键盘的插头

    每一个这样的端口都有它独特的地址

    而它们的地址即是CPU的IN和OUT指令

    这些端口的地址被CPU通过数据总线进行set操作

    同时地址总线仅仅是与RAM相关联的

    然后我们来看看主板是怎么在计算机中发挥作用的

    我们最后要研究的东西是硬盘

    计算机的电源一断开,内存里的数据就全都丢失了

    所以你需要把那些数据储存在可以永久储存的地方

    那就是硬盘

    在硬盘里面有一个覆盖着小型磁铁颗粒的旋转的硬碟和一个有小旋臂的读写头

    旋臂在硬碟的不同位置上

    以将不同的数据储存在不同的地方

    读写头通常移动速度极快

    但绝对赶不上CPU处理数据的速度

    因为这个原因,所有的数据都首先得被读取到RAM中才可以被处理

    所以我们把硬盘装回电脑

    然后我们可以看到刚刚所运行的程序,还有一条告诉用户他猜对了的文本

    所以你现在看到了计算机处理数据最基础的工作过程

    你会在这本书或者这个网站上找到更多关于Scott CPU的信息

    书中的内容或许会和食品中的内容有些小小的不同

    但它们绝不会影响你对所学知识的理解

    你可以在视频的简介中找到关于这些小小区别的介绍

    感谢观看!

    无论你在什么样的道路上前行

    全心投入

    为心中的信念奋斗

    而不仅仅是因为受到驱使

        ---歌罗西 3:23


    在计算机里面你可以看到主板

    绝大多数计算机部件都会连接在主板上

    在主板上有一个微型处理器

    ...to put over hitting...

    如果你把微型处理器拆下来然后看看背面

    你会看到一堆引线,他们连接着主板和微型处理器

    微型处理器由许多不同的区域构成

    他们负责了不同的工作,比如算术运算或者储存数字

    尽管如此这些区域都由同样的元件构成——晶体管

    这里是它的一个3D视图

    晶体管的基础是一个半导体,他有时可以通电,有时不行

    半导体有一个正极区和一个负极区

    除非绿色部分中的导电通路开启,否则两个黄色电极间无法通过电流

    而要让导电通路开启,则需要其上一个裹在玻璃纤维中的电气元件——栅极被通电激活

    连接着该电器元件和两极的分别是另外两个电极区上的电器元件:源极(Source)和漏极(Drain)

    或者说 输入 和 输出

    所有的这些让这个晶体管运行得像金属那样好

    如果输入端被通电激活,除非栅极同样被激活,否则电流无法从输入端流向输出端

    当栅极同样被激活的时候,他会开启下方半导体中的导电通路,这允许电流从源极通过正极流向负极最后从漏极流出

    这一整个组件正常运行的必要条件是所有的电极和电路能够互不干扰独自运行

    这个简单的创意被用于建造逻辑门电路

    举例来说,如果你有两个晶体管,每个晶体管的源极都被通电激活,然后将两个开关分别连接至它们的栅极,然后把这两个晶体管的输出端用电线连接到一个电灯泡上,他们就组成了一个或门。

    或门的符号是这样的。

    无论是打开左边的开关,还是打开右边的开关,还是左右两边的开关同时打开,电灯泡都会亮起。

    如果你修改线路,让第一个晶体管的输出端与第二个晶体管的输出端相连,你就创造出了一个与门

    与门的符号是这样的

    如果你仅仅打开左边的开关,灯泡是不会亮的,因为来自第一个晶体管的电流在第二个晶体管中被截止了

    如果你仅仅打开右边的开关,灯泡同样不会亮起,因为并没有电流从第一个晶体管流出

    在与门中只有两个开关同时被打开输出端才会产生电流

    开关的开启与关闭分别可以被 1 和 0 表示

    0,表示关闭,而 1 表示开启

    同样的对于灯泡的亮灭,有 开启 ,或者说 1 ; 有 关闭,或者说 0

    0 和 1 组成了计算机的语言,他们组成了一个成为二进制的系统

    我们从0000开始,它是有效的因为它完全由0组成

    然后是0001

    然后我们略过一些到0010

    然后0011符合条件

    0012不行

    0013不行

    0014不行

    事实上我们可以以10为单位向上遍历因为中间我们不会再遇到符合条件的数字直到... 0100

    然后0101可以

    然后再次略过一些到0110和0111

    然后是1000

    我们把它拉过来放到下面

    然后可以再次重复右侧的数列

    现在我们把他们连接成一列

    我们可以把这些数字分开然后在最下面放置对应的灯泡

    如果你记得早先我们所讲过的

    我们可以分别用1 或者0 表示灯泡的亮灭

    所以我们从全0即全关闭开始

    然后是1,较远右端的灯泡亮起

    然后是十进制的2

    记住,我们使用0和1表示二进制,1和0

    这意味着我们必须以和十进制不同的方式开始计数

    11等于十进制的3

    100是4

    然后如此这般这般如此

    事实上这远比看上去简单

    如果只有一个灯泡亮起,该灯泡组所表示的数值就等于灯泡它本身所表示的特定的数值

    比如

    二进制的1也等于十进制的1,也就是第一个灯泡的数值

    二进制10的值等于十进制的2

    二进制100的值等于十进制的4

    二进制1000的值等于十进制的8

    你只要把这些亮着的灯泡所表示的值加起来总数就等于灯泡组所表示的十进制数值

    举例来说

    1001的话,有一个数值8的灯泡和一个数值1的灯泡,总计为十进制的9

    1010就是灯泡8加上灯泡2,即十进制的10

    所以1011就是8+2+1等于十进制11

    所以二进制的1+1等于十进制的2

    既然开关同样可以用1和0表示,我们把开关放在左边,中间放个加法机,然后看看怎么运用我们先前提到的逻辑门来达到加法的目的

    这个加法机成为完整加法机,因为它由两个半加法机所组成,我们过一会就会了解到为什么

    现在在半加法机内部,要让右边左侧的灯泡亮起,我们需要一个与门

    要让右边右侧的灯泡亮起,我们使用一个异或门,简称XOR

    异或门的工作效果正是你所期待的,

    一个开关打开或者另一个开关打开,输出端都能得到正确的结果

    如果都打开或者都关闭,那么灯泡也都灭了

     现在,让我们来看看异或门是由什么所组成的

    里面有一个或门,或非门,还有一个与门

    啥是或非门?它是一个与门和一个非门的组合

    非门让输出端的结果恰好与输入端相反

    与非门仅在两个开关都打开时输出关闭的信号

    这同与门正相反

    这是异或门内的关键部分

    现在我们来看看当我们仅打开一个开关时会发生什么

    或门和与非门的输出都是1,所以与门的输出也是1,所以右侧的灯泡亮起了

    如果我们关掉这个开关并打开另一个开关

    我们会得到一样的结果

    如果我们打开两个开关,异或门输出了0

    但那个独立的与门输出了1

    所以左侧的灯泡亮起了

    这就是1+1 = 10

    另外要考虑的事情是,就像 1+1 = 10 一样,7+7 = 14,我们需要考虑进位的情况

    事实上在二进制里进位的过程与十进制类似

    现在你有一堆完整加法机彼此相邻

    左侧的灯泡代表了即将进到下一位的数值

    所以我们移除左侧的灯泡

    然后把它的引线接到下一个加法机

    然后把前一个加法机的对应引线接入当前的加法机

    这一条接入的引线就是我们需要另外一个半加法机的原因

    这样我们就基本构成了一个完整加法机

    两个半加法机中独立的与门的输出端连接到同一个新的或门的输入端上

    或门的输出端即原来连接左灯泡的对应进位的引线

    所以现在我们把两个开关都打开

    如果前一个完整加法机的进位引线被激活了

    我们这里就相当于 1+1 = 10,同时再加上之前进位的 1

    或者说 1+1+1,二进制中等于11,或者十进制中的3

    我们这里能够看出结果是11,因为进位引线和右侧灯泡都被激活了

    把所有的输入端都关闭,输出端同样也关闭了

    现在我们来关注 完整加法机 这个单元

    或者从现在开始我们可以叫它 加法机

    然后我们把引线做上标记,这里用A和B代表两个开关,用Sum表示右侧灯泡

    我们把这三个部件都移开

    把这些加法机旋转一下,摆正标签

    现在我们有八个加法机一字排开

    然后从右到左将他们用序号标上,所以我们就可以把它们区别开了

    分别在每个加法机上面接入两个开关,代表A和B

    然后再次把灯泡分别接上他们的输出端

    这里的灯泡同样可以用十进制数字进行标记

    1,2,4,8,然后不断地乘以2,就16,32,64,和128

    现在从画面左侧划出一块区域用以看出我们正在做的事情

    我们先把开关和灯泡所表示的二进制数值列举出来,然后挂上一个加号

    然后标上对应的十进制数值

    然后我们把每个加法机部件里的输入输出都标记上对应的激活状态,1和0,代表着激活与关闭

    然后我们来看看当1+1时会发生什么

    在加法机A里,A和B激活了,但CI是关闭的

    所以是1+1+0,在二进制中结果为10,进一位,并且SUM结果为0

    在加法机B中,A和B为0,但CI为1

    所以加法机B的Sum为1

    再来一些例子这样你就可以看得更加仔细

    来看看2+2

    在加法机A中没有输入端被激活

    所以Sum和CO都为0

    在加法机B中A和B都为1,所以CO为1

    在加法机C中CI为1,这使得灯泡4亮起了

    即:2+2 = 4

    3+3,加法机A向加法机B进一位,使得加法机B进行的是1+1+1,结果为二进制的11

    所以SUM和CO也就那样了

    所以加法机B和加法机C的灯泡就亮起了

    所以3+3 = 6

    再来一些例子

    你可以自己试试如果你有兴趣的话

    这里是10+10

    然后我们看看15+12

    最后,219+36

    所有的灯泡都亮起了,结果是255

    这样的过程每秒钟发生了几百万次

    在你的微型处理器里,在你的主板上,在你的计算机中

    所以现在你在一节课中看到了

    “计算机是怎样进行加法的”

  • 相关阅读:
    js返回上一页并刷新思路
    C#字符串拼接
    html2canvas截图问题,图片跨域导致截图空白
    VS2017未能添加对"System.Drawing.dll"的引用
    微信小程序云开发获取文件夹下所有文件
    js解析json报错Unexpected token i in JSON at position 1
    人工智能学习
    suricata的模块和插槽
    学习助手开发(二)——表单排序
    成功在Caterpillar代码中插入事件对象-20200917
  • 原文地址:https://www.cnblogs.com/Chorolop/p/11476971.html
Copyright © 2011-2022 走看看