我无法理解我们看到的字符之间的抽象以及计算机如何处理它们

I cannot understand the abstraction between characters we see and how computers treats them

水平很低,英语不是我的母语所以请放轻松。

想象一下您在 bash 中,命令提示符出现在您的屏幕前。

当您键入 ls 并按回车键时,您实际上是在向 cpu、01101100 01110011 00001010(即:ls linefeed) 从你的电脑吧?键盘控制器将字节发送到 cpu,cpu 告诉操作系统已接收到哪些字节。

所以如果我理解正确的话,我们的硬盘(或内存)中有一个名为 01101100 01110011 的应用程序?那是一个文件,而且是一个可执行文件。但是操作系统如何在驱动器或内存中找到01101100 01110011呢?

另外我想把这个问题扩展到函数。例如,我们说 C 标准库有一个名为 printf 的函数。一个函数怎么能有一个在文件中的名字呢?好的,我知道 printf 函数的实现是 cpu 和特定于操作系统的,是一些位于内存或硬盘驱动器某处的机器指令。但我不明白我们是如何做到的?

当我link一个需要执行printf的代码时,它是如何找到的?我假设操作系统对函数的名称一无所知,或者它不知道?

如果您查看各个位或字节以及 CPU,您将很难找到答案。

事实上,如果您键入 ls,这些字符的 ASCII 码将被 shell 读取并组合到 字符串 "ls"。那时 shell 已经用 string 键构建了一个 dictionary ,它找到了键 "ls" 并且它发现这指向路径中的特定可执行文件 "ls",如“/usr/bin”。

你看,即使 shell 也在 字符串中思考 而不是字符、字节甚至位。

当链接器尝试从您的代码和一组库文件(*.lib、*.dll)构建可执行文件时,链接器内部会发生非常相似的事情。它构建了一个 字典 ,其中 "printf" 作为键之一,它指向正确的库文件和该文件的字节偏移量。 (这个比较简单,为了演示原理)

在所有这些到达 CPU 之前,有几层库(和 BIOS 代码)。不要把自己的日子过得太苦,不要对这些层次想太多细节。

Koray,用户@DrKoch 给出了很好的答案,但我想添加一些抽象。

首先,ASCII是一种编码。它是一个 table,其中一列是位模式,下一列是字母。位模式恰好是一个字节长(不包括 'wide chars' 等等)。如果我们知道一个字节应该代表一个字符,那么我们可以在 table 中查找字节的位模式。打印函数(还记得点阵打印机吗?)接收一个字符(一个字节)并指示点阵打印机的针以某种有序的方式敲打在纸上,看,形成了一个人类可以阅读的字母。设计 ASCII 码是因为计算机不使用字母进行思考。还有其他的代码,比如EBCDIC,只是表示table不同。

现在,如果我们不知道字节是某种代码中字母的表示,那么我们就迷路了,字节可能只表示数字。我们可以将一个字节与另一个字节相乘。所以你可以将 a' 与 'p' 相乘,得到 97 * 112= 10864。那个男性有感觉吗?只有我们知道字节代表数字,如果字节代表字符是无稽之谈。

下一个级别是我们将所有应该表示字母(字符)的字节序列称为 'string',并且我们开发了可以搜索、获取和附加 from/to 字符串的函数。一个字符串有多长?在 C 语言中,我们一致认为,当我们看到位模式全为零的字节(空字符)时,就到达了字符串的末尾。在其他语言中,字符串表示可以具有长度成员,因此不需要终止空字符。

这是一个 "stacking of agreements" 的例子。另一个例子(指你之前问的一个问题)是中断:硬件在电路板上定义了一条物理线作为中断线(协议)。它连接到处理器的中断引脚(协议)。线路上的信号(例如来自外部设备)导致处理器保存寄存器的当前状态(协议)并将控制权转移到预定义的内存位置(协议),其中放置了中断处理程序(协议)来处理来自外部设备的请求。在这个堆叠的例子中,我们可以在功能应用上有很多层次,在各个门和晶体管下面有很多层次(以及多少伏特是“1”和多少伏特是“0”的基本定义,以及在确定看到 1 或 0 之前必须观察到该电压多长时间)。

只有理解了所有这些层次只是协议,才能理解计算机。只有理解所有这些级别只是人类之间达成的协议,才能从中抽象出来,而不会被这些基础所困扰(工程师会照顾他们)。