计算机基本概念一日一贴

xiaobu 1月前 119

为了帮助基础薄弱的学员快速掌握一些基本的概念和技巧,我发表这个帖子,希望能在最短的时间内让学员快速对基础概念有正确的理解。

 

比特(bit)和字节(byte)


比特和字节是任何想理解计算机的人必须先掌握的最基础的两个概念。下图展示了这两个概念和相互之间的关系:

我们熟知的十进制是“逢十进一”,类似的,计算机普遍采用的是二进制,它的基本规则是“逢二进一”。十进制有0~9十个基本数字,类似的,二进制只有两个基本数字,0和1。一个二进制数字中的一位即为一个比特,英文单词是bit。连续的8个比特组成一个字节,英文单词是byte。当一个程序读写内存或者磁盘上的文件时,它能访问的最小单位是字节,而不是比特,即:一个程序要么读取一个字节,要么写入一个字节,不能只写入一个比特或者只读一个比特。字节是计算机软件的细胞。

 

知道了这两个基本概念,我们可以想象出如下的图,计算机的内存和磁盘上保存着连续的0和1的比特,我们把它们按照8个一组进行分组,就变成了连续的字节,如下图所示。

类似的,当我们觉得字节还是太琐碎,我们可以把n个字节作为一个基本单位。譬如PG里面就把8192个字节作为一个整体进行读写。

一个字节的作用是有限的,必须是多个连续排列的有序的字节才能有更大的作用。在一个有序且连续的字节队列中,每个字节有两个基本属性:这个字节所能表示的值(value),和这个字节在这个队列中的位置(position)。字节的位置又被称为“地址”(address)或者“偏移量”(offset)。

最新回复 (9)
  • xiaobu 1月前
    引用 2

    一个字节可表示的数值范围


    我们已经知道了一个字节由8个比特组成,那么这个字节所能表示的数值的最小值和最大值分别是多少呢?我们看下图:

    很自然,如果这8个比特全部为0,则表示最小值0,如果这8个比特是全1,则表示最大值,这个最大值就是255。你可以拿出笔和纸进行演算一下。

    所以我们可以放心大胆地说:

    一个比特能够表示的最小值是0,最大值是255。

    当然,我们很快发现一个问题:如果我想表示负数,如-1,-3等,如何做呢?上述的图表示的是无符号整数,就是不考虑负数的情况。如果要表示负数,则可以将最高位,即最左边的那个比特作为符号。如果这个比特是0,则表示正数,也包括0;如果这个比特是1,则表示负数。这个问题比较复杂,我们在这里不进行讨论。有兴趣的同学可以自行探索。

     

  • xiaobu 1月前
    引用 3

    文件(file)和偏移量(offset)


    在理解了比特和字节这两个基本概念以后,我们再加入一个概念:队列。我们在军训或者阅兵式上看到士兵会站成一排,从左到右,整整齐齐。队列就是一个一维的组合,在我们日常生活中也随处可见。队列是一维的,有序的,这是队列的基本特征。

     

    在计算机中,我们要频繁地和文件打交道。什么是文件呢?其实文件有一个异常简单的模型:即

    在计算机中,任何文件,均是由n个字节组成的有序的队列。其中n的取值范围是1到几十亿。

    我们可以使用下图来表示任何文件的结构:

    在上图中,一个单元格就是一个字节。n个字节从左到右,整整齐齐地站成一排,就是任何文件的基本结构。我们对这些有序的字节进行编号,第一个字节的编号为0,它右边紧挨的字节编号为1,以此类推,所以最后一个字节的编号是n-1。每个字节的编号被称为这个字节的地址(address)或者偏移量(offset)。

     

    知道了这个模型后,如果我们想从一个文件中提取若干连续的字节,我们需要提供两个信息:你需要提取多少个连续的字节?你想提取的连续字节中的第一个字节的偏移量是多少?有了这两个信息,我们就可以从一个文件中提取所需要的信息了。

    展示一个文件内容的基本工具是hexdump,它有两个参数,-s表示跳过(skip)多少个字节,-n表示你要提取的字节的个数,即长度。

    有兴趣的同学可以研究一下Linux下如何使用hexdump工具。

     

     

     

     

     

     

  • xiaobu 1月前
    引用 4

    内存的布局


    内存和文件的模型是一样的,都是n个字节组成的有序的,一维的队列。所以我们可以使用下图来理解内存:

    内存和文件的不同点在于:读写内存的速度远远大于读写文件的速度,因为文件是存放在磁盘上的。磁盘的读写速度比内存的读写速度慢几十万倍。另外一个不同点是:当断电后,内存中的信息就消失了,但磁盘上的内容在断电后不会消失。

     

    我们可以用一个有序的,一维的字节队列来表示任何文件和任何内存。这个模型非常简单、统一。它是我们学习任何计算机知识时必须理解和牢记的基本模型。

  • xiaobu 1月前
    引用 5

    64位的虚拟内存


    当我们谈论内存时,要注意区分两个概念:物理内存(phsical memory)和虚拟内存(virtual memory)。

    物理内存是比较容易理解的,我们选购手机或者电脑时,内存的大小是一个需要考虑的重要指标。譬如我的iPhone手机内存32GB,这里的32GB就是指物理内存,一分价钱一分货,你多花钱,苹果就给你更多的内存芯片。

    当一个程序员在编写程序时,他是无法知道未来他呕心沥血写的程序所运行的计算机的物理内存是多大的。无论你的手机有16GB大小的物理内存,还是32GB的物理内存,微信这个程序都可以正常运行,无非是速度有快有慢而已。

    所以程序员在编写程序时,他不会考虑物理内存的差异。在他眼里,或者在他所编写的程序眼里,天下所有的计算机都是一样的,都有固定大小的内存,这个内存是虚拟的,想象中的,被称为虚拟内存。在64位的CPU上,虚拟内存是2^64个字节。如下图所示:

    内存是由n个字节组成的有序的字节队列。64位的虚拟内存,n的值是2^64,即2的64次方。这是一个非常巨大的内存。这是所有的程序员或者程序,或者进程眼中的内存模型。

    如此巨大的内存必然最终要落实在16GB或者32GB这么可怜兮兮的很小的物理内存中运行。这是怎么做到呢?这里面就有一个局部性的规律,即:虽然一个进程眼中的虚拟内存是非常巨大的,但是每时每刻,计算机指令只在一个局部内存中进行执行。有了这个规律,操作系统和硬件配合,在后台默默地倒腾,给进程形成了一个该进程拥有巨大虚拟内存的假象。

     

    所以我们头脑中要有虚拟内存和物理内存这两个不同的概念。

     

  • xiaobu 1月前
    引用 6

    什么是进程?


    进程(process)就是正在运行的程序(program)。进程是由一个有序的指令序列。在下图中,每个I都是一个指令,随着时间的流失,已经被执行的指令形成了一个有序的指令流。

     

  • xiaobu 1月前
    引用 7

    源代码,编译和可执行文件


    程序员努力工作的内容就是敲键盘,输入a-z, A-Z,0-9等字符。这些字符组成的程序被称为“源代码”(source code)。这些源代码是人类的工作成果,也是人类能够读懂的程序。但这种程序并不能被计算机所理解。计算机能理解和执行的程序是0101001111等比特流。所以我们需要把人类创造且理解的程序变换成计算机能够理解的程序。这种变换过程被称为“编译”(compile),如下图所示:

    我们知道PostgreSQL几乎百分百是由C语言编写的,它需要被编译器(compiler)进行编译,产生最终的二进制程序,这些二进制程序就可以被计算机理解和执行。

    当我们写了一个c语言程序后,我们可以使用“gcc -Wall xxxx.c -o yyyy”命令来把源程序xxxx.c编译成二进制的可执行的程序yyyy。计算机就可以执行yyyy这个程序。这个程序被称为可执行文件(executable)。

     

  • xiaobu 1月前
    引用 8

    十进制、二进制和十六进制之间的转换


    十进制、二进制和十六进制是计算机中最常见的三种进制。十进制有0-9十个基本数字,逢十进一。二进制有0和1两个基本数字,逢二进一。十六进制有0-9,ABCDEF共16个基本数字组成,逢十六进一。

    在这三种进制转换时,我们把二进制数按照四位一组进行分类,如果不足四位,就在左边补上前导零,凑够4位。然后我们使用如下表格进行转换:

    所以大家要牢记上面的转换表。在其中,二进制和十六进制之间的转换是最容易的。有一些规律大家可以牢记:

    1. 每位十六进制的数字代表4位二进制的数字。
    2. 每个字节是8个比特,所以每个字节可以由两位16进制的数字组成。

    还有一些基本规律,大家可以记住:n个比特,可以表示2^n个不同的数字,即2的n次方。譬如1位比特可以表示0和1两个数字,即2^1,4个比特表示16个不同的数字,从0到15,即2^4。8个比特,一个字节,可以表示256个不同的数字,从0到255,即2^8。4个字节可以表示2^32个不同的数字,8字节可以表示2^64个不同的数字。

    对于1、2、4、8、16、32、64、128、256、512、1024、2048、4096、8192等2的幂次方的数字,它们有一个特殊的形态,即它们的二进制都是最高位位1,其余位为0.

    对于1、3、7、15、31、63、127、255、511、1023、4095、8191等数字,它们的二进制全部是1组成,没有0。

     

    这些有趣的规律,希望大家用笔和纸演算一下,记在心里。

    当我们表示一个数是十六进制时,会在这个数之前加上0x,譬如0x1234,这个0x是个前缀,其语法来自C语言。

     

     

     

     

     

  • xiaobu 1月前
    引用 9

    UTF8编码的规律


    我们要区分“字节”(byte)和“字符”(character)两个截然不同但又有紧密关系的概念。字节的概念比较容易理解,它是8个比特组成,可以表示0-255共计256个不同的数字。字符是一个语言概念,譬如:'A'是英文中的一个字符,‘9'是一个各种语言都可以接受的数字字符,“国”是汉语的一个字符,即一个汉字就是一个字符。

     

    在计算机中,万物皆数字。每个字符都用一个数字来表示。为了简单,我们当然希望一个字节所表示的数字对应一个字符。这个在英语世界中很容易实现,因为英语就是26个字母,大小写共计52个,再加上十个数字,再加上譬如!@#$%^&*()等常用的符号,可以用127个不同的数字来表示它们,所以一个字节就是一个字符。这种设计方案被称为ASCII码表。在ASCII码表中,一个字符就是一个字节所表示的数字,大家可以在网上很容易找到ASCII码表。

     

    但是对于其它国家的语言,譬如汉字,有几千个,不可能用一个字节来表示一个汉字,因为一个字节最多可以表示256个不同的数字。所以我们要采用多个字节来表示一个汉字。

    UTF8是最流行的编码方案,它是可变长的编码,就是可能一个字节表示一个字符,也可能2个字节表示一个字符,最多的情况下,4个字节表示一个字符。下图是UTF8编码的规律:

     

    在上图中,x表示一位比特,其值可以是0,也可以是1。我们可以看到,如果我们拿到一个字节序列,如何判断它们是合法的UTF8字符呢?算法很简单,我们从头开始依次读取每个字节,根据如下规律来判断这个字节是否属于UTF8字符:

    1. 如果这个字节最高位的比特是0,则这个字节就是一个UTF字符。这个规则让UTF8完全兼容ASCII码,这是UTF8为什么流行的重大原因。
    2. 如果这个字节的最高位比特是1,则看看最高三位是否是110,如果是,则再读取下一个字节,看看它的最高两位是否是为10,如果是,则这两个字节组成了一个UTF8字符。
    3. 如果上一条规则不满足,则判断第一个字节的最高4位比特是否是1110,如果是,则再读取后面两个字节,判断它们的最高2个比特是否是10,如果是,则这三个字节组成了一个UTF8字符。
    4. 如果上一条规则不满足,则判断第一个字节的最高5位比特是否是11110,如果是,则再读取后面三个字节,判断它们的最高2个比特是否是10,如果是,则这四个字节组成了一个UTF8字符。

    如果不满足上述4条规则的字节组合,均不是UTF8字符。

    根据上图的描述,我相信你很容易设计一个程序来判断给定的一个字节序列是否为UTF8字符。

     

     

     

  • xiaobu 25天前
    引用 10

    大端(Big-Endian)和小端(Little-Endian)的概念


    我们可以用下图来理解这两个概念:

     

    内存是一个一维的数组,里面每个单元格都可以保存一个字节的信息。在内存中每个字节都有一个地址,譬如5号单元格的地址是5,6号单元格的地址是6。因为5小于6,所以地址5被称为“低地址”,地址6被称为“高地址”。如果一个内存字节的地址是a,则a+1是高地址,a为低地址;类似的,a-1则为低地址,a为高地址。

    理解了“高地址”和“低地址”的概念后,假设我们有一个数,譬如AB(十六进制),它可以放在一个内存单元格中。如果一个十六进制数0x1A2B,就需要2个连续的内存单元格存放这个数。这里就存在一个问题:1A是高位字节,2B是低位字节。如果把1A放在高地址的内存单元格中,2B放在低地址的内存单元格中,则我们称这种的内存安排为“小端”。我们可以用一句口诀来记忆什么是小端:高位放在高地址,低位放在低地址

    与之相反,我们也可以把一个数的高位字节放在低地址的内存单元格中,低位字节放在高地址的内存单元各种。这种内存布局被称为“大端”,即:高位放在低地址,低位放在高地址

    在上图中,上方的内存布局是“大端”,下方的内存布局是“小端”。

    通常我们遇到的X86/X64和ARM的CPU都使用“小端”布局,所以我们使用hexdump等工具显示一个数据的原始面貌时,为了计算这个数的大小,存在一个反转的动作。

返回
发新帖