计算机组成原理

4/27/2024 Computer Fundamentals

该笔记是计算机组成原理(未完成,完成时间大约在5-6个月,大约每2天更新一次)

ALU(算术逻辑单元)

Cache(高速缓存)

# 第一章 计算机概述与计算机技术

# 1.1 引言

# 计算机的分类及其特性

​ 我们大致将计算机分为三种

  1. 个人计算机(PC)
  2. 服务器
  3. 嵌入式计算机

个人计算机

​ 个人计算机,强调的就是对于单个的用户提供服务,要有一个良好的性能,要有一个低廉的价格 。

服务器

​ 服务器,其实就是一个大型计算机。虽然我们常用的是云服务器,但是他背后的本质就是一个大型的硬件设备,我们用户借助网络去访问这个服务器。

​ 服务器具有更强的计算能力,但是从制造技术来说,其实跟计算机差不多.只是相对而言能力更强, 但是,一旦出了故障,后果也会更加严重。

​ 最高端最高端的服务器,就是我们经常听说的超级计算机。超级计算机是服务器领域中最为强大和复杂的例子,它们由成千上万个处理器核心组成,并通过高效的并行计算架构和专门定制的互联网络进行协同工作。

嵌入式计算机

​ 所谓嵌入式,其实就是把一个小芯片,带着一些小程序,嵌入到其他设备里面进行计算,都叫做嵌 入式计算机。

​ 嵌入式一般对于成本会有要求,所以一般不会像电脑CPU这么强大.一般都是只用于某一种特定的功 能。

# 计算机的进一步发展

个人移动设备(PMD)

​ PMD也就是我们日常生活中所使用的手机平板等设备。PMD通过无线网连接,无需复杂的物理连线,进一步增强了其便携性和灵活性。PMD的输入也进行了革命性的变化,从键盘鼠标的输入改为了触摸屏输入等更加直观、便携的交互。

云计算

​ 云计算正在逐渐替代传统的服务器模式,它通过仓储级别计算机(WSC)和数据中心,实现了软件 即服务(SaaS)的概念。这一变革对于软件工业来说是一个革命性的进步。

​ 云计算的发展与普及,可以让企业不再自行建立和维护昂贵的数据中心,降低了成本。同时也让普通用户能通过PMD随时随地访问各种云服务,如在线办公、社交娱乐、数据存储等。这使得我们的生活更加便捷和高效。

​ 然而,随着云计算的普及,也带来了一些新的挑战,如数据安全、隐私保护等问题

# 1.2 计算机结构系统

​ 我们的前辈提出的8个非常伟大的硬件设计思想.以至于到了现在,我们在进行处理器的设计的时候依旧在使用这些设计思想.这些思想也会贯穿于我们整个计算机组成原理的学习中

面向摩尔定律的设计(目前已经废除)

​ 摩尔定律,由Intel的创始人之一戈登·摩尔在1965年提出,它指出在一个芯片上集成的晶体管数量 每18到24个月就会翻一番。

​ 摩尔定律的出现是基于当时的技术、经济和设计挑战。尽管它曾经是一个有效的预测工具,但随着技术的不断进步和物理学的限制,摩尔定律已经不再完全适用。然而,它仍然对计算机行业产生了深远的影响,并推动了持续的创新和发展。

使用抽象来简化设计

​ 抽象之所以能够提高我们的效率,主要是因为它允许我们关注问题的核心,而不是被不 必要的细节所困扰。

​ 抽象还有助于降低系统的复杂性。通过将系统分解为不同的抽象层次,我们可以更好地理解 系统的整体结构和行为,从而更容易地发现和解决问题。这种分层的抽象方法也使得系统更加模块化, 易于扩展和维护。

加速大概率事件

​ 观点非常核心,实际上这是计算机科学和工程中的一个普遍原则:优化瓶颈(Bottleneck Optimization)。 在许多系统中,大部分的时间或资源往往被少数几个关键操作或组件所消耗。这些操作或组件就构成了系统的瓶颈,它们限制了系统的整体性能或效率。

​ 与其在细节上花费大量精力进行微小的优化,不如集中资源来优化这些关键的操作或组件。这种策略往往能够带来更大的性能提升和更好的整体效果。

​ 也正是由于这个思想,我们诞生出了非常多有名的策略.比如缓存,比如数据压缩,比如预处理,预 测和推测执行等等

  • 缓存:是一种常用的优化技术,用于加速数据的访问速度。通过将频繁访问的数据存储 在高速缓存中,可以减少对慢速存储设备的访问,从而提高整体性能。
  • 数据压缩:在数据传输或存储时,通过压缩数据可以减少所需的空间或带宽。这有助于 减少I/O操作、提高存储效率,并加速数据传输。
  • 预处理:通过提前对数据进行处理或计算,可以避免在实时处理时的复杂性和延迟。预 处理可以包括数据清洗、索引创建、聚合计算等。
  • 预测和推测执行:这些技术试图提前预测或猜测未来的操作或需求,从而提前进行准备 或执行。例如,预测模型可以预测用户的下一个操作,而推测执行则可以尝试提前执行 某些操作以提高响应速度。

通过并行来提高性能

​ 从计算机诞生到现在,包括我们日常的学习,并行随处可见 。也是这门课非常重要的部分。

通过流水线来提高性能

​ 计算机体系结构中,流水线(Pipeline)是一种经典的并行处理技术,它将一个复杂的计算任务拆 分成多个连续且相对独立的阶段,并让这些阶段像工厂流水线一样依次执行。每个阶段专注于处理任务 的一部分,一旦一个阶段完成其工作,数据就会立即传递到下一个阶段,而不需要等待整个任务全部完 成。

​ 流水线也面临着一些挑战和限制。例如,流水线的性能受到流水线深度的限制,即流水线中 分的阶段的数量。如果流水线过深,可能会导致处理器的延迟增加,从而影响性能。此外,流水线的效 率还受到指令之间数据依赖关系的影响,如果指令之间存在数据依赖关系,那么它们就不能在流水线中 并行执行

通过预测来提高性能

​ 预测技术是计算机系统结构设计中提高性能的重要手段之一.预测技术通过预测未来的指令或数据,提前进行预取和预处理,从而减少等待时间和提高处理速度.预测技术通常应用于多个领域,包括指令预测、分支预测、缓存预取等。

指令预测 指令预测是一种预测技术,用于预测下一个要执行的指令。处理器可以基于之前的指令序列 来预测未来的指令,并提前进行解码和准备。这样,当实际的指令到达时,处理器可以更快地执 行它,从而提高执行速度。

分支预测 分支预测是另一种常见的预测技术,用于预测条件分支指令的结果。处理器会尝试预测分支 指令是否会被执行(即条件为真或假),并提前加载和执行相应的指令。这样,当分支指令的结 果确定时,处理器已经准备好了要执行的指令,从而避免了延迟。

缓存预取 缓存预取是一种预测技术,用于预测哪些数据将被访问,并提前将这些数据加载到缓存中。 处理器可以基于之前的访问模式来预测未来的数据访问,并提前从内存中加载这些数据到缓存 中。当实际访问这些数据时,它们已经位于缓存中,从而提高了访问速度。

​ 预测技术也存在挑战和限制。预测算法可能出错,导致预测不准确。当预测失误时,系统需 要进行恢复操作,这可能会引入额外的延迟和开销。因此,设计预测算法时需要权衡预测准确率和恢复 操作的代价。

存储器层次结构

​ 存储器层次结构是计算机系统设计中的一个核心概念,旨在解决不同存储器设备在速度、容量和价 格之间的矛盾。这种层次结构通过将不同类型的存储器按照其特性和成本效益组织成多个层次,从而实 现了高效的数据访问和管理。

  1. 最顶层通常是寄存器(Registers)或者缓存(Cache),它们速度最快,但容量最小, 且单位容量的成本最高。CPU可以直接访问寄存器,而高速缓存则用来临时存储最近频 繁使用的数据或指令以减少主存访问次数。
  2. 下一层是主存(RAM,Random Access Memory),其速度相较于寄存器和缓存慢, 但容量大得多,价格也相应降低。主存是CPU直接寻址和交换数据的主要区域。
  3. 再下一层可能包括虚拟内存(Virtual Memory)、磁盘缓存(Disk Cache)等,它们的 速度进一步下降,但提供更大的存储空间,同时价格相对更低。
  4. 底层则是硬盘驱动器(HDD)和固态硬盘(SSD)等永久性存储介质,它们的访问速度 最慢,但提供了TB级别的大容量存储,并且单位存储成本最低。

​ 通过这种分层设计,当程序需要数据时,优先从最快的存储层级查找,如果未命中,则逐级向下查 找,并在较高级别存储中保留一份副本,以便后续再次访问时能更快地获取数据。(也就是让最常用的 数据离CPU最近)这样,即使是最慢的存储层也可以提供足够的存储空间,而最快的层保证了大部分时 间内的高效数据访问,从而提升了整体性能并平衡了各种需求之间的矛盾。

过冗余提高可靠性

​ 我这个时候我们就通过冗余部件的方式来提高可靠性.也就是通过增加额外的硬件组件来提高系统的可靠性和容错能力。

​ 冗余部件的引入意味着在系统中增加了额外的硬件组件,这些组件通常是关键部分(如处理器、存储器、通信通道等)的复制品,它们处于待命状态,一旦原始组件发生故障,冗余组件可以立即接管工作,从而确保系统的连续运行和数据的安全性。

常见的冗余方式

  1. 处理器冗余:使用多核处理器或者构建集群系统,当一个核心或节点失效时,其他部分 可以接管任务,保证服务不中断。例如,云计算平台中的服务器集群就广泛采用了这种 策略。
  2. 存储冗余:RAID(Redundant Array of Independent Disks)技术是一种常见的存储冗 余方法,通过磁盘阵列将数据分布在多个硬盘上,实现数据备份、错误校验以及在单个 硬盘故障时仍能正常读写数据。
  3. 网络冗余:在网络设备中设置备用路径或备用设备,如负载均衡器、交换机的链路聚合 (Link Aggregation)、路由协议中的热备路径等,当主路径出现问题时,流量可以迅 速切换到备份路径或设备。
  4. 电源冗余:数据中心通常会采用双路供电或多路供电系统,确保一路电源故障时另一路 电源能够继续为服务器和其他关键设备供电。
  5. 冷却冗余:大型数据中心还会对冷却系统进行冗余设计,以防止因冷却系统故障导致服 务器过热而宕机。

​ 这个方式随处可见,比如每次双十一,阿里巴巴都会弄很多台服务器,一台服务器崩了,另一台立 刻接管.包括我们日常生活,汽车都会搞个备用轮胎等等的意思是一样的。

总结八种硬件设计思想

  1. 面向摩尔定律的设计:指出在一个芯片上集成的晶体管数量每18到24个月就会翻一番。(目前已经废除)
  2. 使用抽象简化设计:使用抽象用来表示不同的设计层次,高层次的看不见低层次的细节,只能看见一个简化的模型。
  3. 加速大概率事件:是优先关注那些对系统性能影响最大的操作或组件,并对其进行优化。通过集中资源和精力在这些关键点上,我们可以更有效地提升系统的整体性能和效率。
  4. 通过并行来提高性能:
  5. 通过流水线来提高性能:流水线(Pipeline)是一种经典的并行处理技术,它将一个复杂的计算任务拆分成多个连续且相对独立的阶段,并让这些阶段像工厂流水线一样依次执行。
  6. 通过预测来提高性能:预测技术通过预测未来的指令或数据,提前进行预取和预处理,从而减少等待时间和提高处理速度。
  7. 存储器层次结构:旨在解决不同存储器设备在速度、容量和价格之间的矛盾。这种层次结构通过将不同类型的存储器按照其特性和成本效益组织成多个层次,从而实现了高效的数据访问和管理。
  8. 通过冗余提高可靠性:是通过增加额外的硬件组件来提高系统的可靠性和容错能力。

# 1.3 程序

​ 我们编写程序的时候都是使用高级语言,但是计算机不能直接读懂这些高级语言,计算机只能读懂机器语言。机器语言是一种非常底层、特定的指令集。

​ 应用程序到硬件执行要系统软件来介入。系统软件的主要作用是为应用程序和硬件之间提供一个桥梁,使得应用程序能够被硬件所识别和执行。

  • 系统软件:提供常用服务的软件,包括操作系统、编译程序、 加载程序和汇编程序等。
  • 操作系统:一个关键系统软件,它为用户提供了与硬件交互的界面,并管理计算机的各种资源。同时,操作系统还提供了很多其他的功能,如任务调度、内存管理、设备驱动等,以确保计算机的稳定运行和高效使用。
  • 编译程序:责将高级语言编写的源代码翻译成机器语言,以便硬件能够执行。

从高级语言到硬件语言

​ 以c语言为例,从高级语言到汇编语言的过程

image-20240415195347155

  • 机器语言:计算机可以将二进制代码存储在内存中,并且执行这些代码。 二进制位:也称为位(bit), 基数为2 的数字中的 0 或 1,它是信息的基本组成元素。 指令:计算机硬件所能理解并服从的命令(也是二进制位串)。 在计算机内部,指令和数据并没有本质的区别,它们都是以二进制形式存储的(区别在后面提到)

  • 汇编语言:以助记符形式表示的机器指令。

  • **高级语言:**高级编程语言 C+ +、 Java 等可移植的语言,由一些单词和代数符号组成,可以由编译器转换为汇编语言。

# 1.4 硬件的概念

​ 任何一台计算机,都会完成一些通用的基础的操作:输入数据、输出数据、处理数据存储数据

​ 完成这些操作需要硬件的支持,有5大基本部件:

  1. **输入设备:**向计算机提供信息,比如键盘,比如鼠标。
  2. **输出设备:**将计算结果呈现给用户,比如显示器。
  3. **数据通路(运算器):**主要包含算术逻辑部件和通用寄存器等,其功能是用来执行算术和逻辑运算等操作。
  4. **控制器:**对指令进行译码,生成相应的控制信号,以控制数据通路进行正确的操作。
  5. **存储器:**存储数据。包含内存(主存储器)和外存(外部存储器)。

注:

1.控制器和数据通路统称 (中央)处理器,即CPU

2.输入输出设备也叫外设(外部设备),即I/O设备。外设通常由机械部分和电子部分组成,而且两部分通常是可以分开的,机械部分是外部设备本身,而电子部分则是控制外部设备的I/O控制器或I/O适配器。

3.各个部件通过总线连接。

4.处理器从存储器中得到指 和数据,输入部件将数据写入存储器,输出部件从内存中读出数据,控制器向数据通路 存储器、输入和输出部件发出命令信号

5.对数据通路的解释:数据在功能部件之间传送的路径称为数据通路,路径上的部件称为数据通路部件,如ALU、通用寄存器等。

image-20240416212708681

  • 显示器:I/O设备中的输出了,就是给我们呈现出画面的东西。

  • 触摸屏:平板电脑和智能手机则是使用了触摸屏代替键盘鼠标。

  • 集成电路(芯片):

  • CPU芯片

  • 内存芯片:内存是程序运行时候的存储空间,同时也用于保存程序运行时所需要使用到的一些数据。内存其实就是一块DRAM芯片(动态随机访问存储器),用来存储程序的指令和数据。

  • cache:高速缓存,采用的是另一种技术,叫做SARM(静态随机访问存储器)。

  • 外存:早期占据主导地位的是磁盘,包括硬盘、光盘和软盘等。它们都利用磁性来记录

  • 数据。随着技术的发展,非易失性半导体存储器——闪存(flash memory)在个人移动设备中逐渐取代了磁盘。U盘也是闪存的一种形式。

  • 与其他计算机进行通信:计算机网络。

  • 处理器和存储器制造技术:芯片制造过程。

image-20240416213022624

# 1.5 计算机性能

​ **计算机的性能:**论其在执行特定任务时的效率和能力。根据用户不同,需求不同,处理的东西不同,所要求的性能是不一样的。

  1. **响应时间(执行时间):**从用户发起一个请求或指令到系统完成该请求并给出反馈所需的时间。包括硬盘访问、 内存访问、I/O 活动、操作系统开销和 CPU 执行时间等。
  2. 吞吐率(带宽):这是衡量系统在一定时间内能处理多少工作量的能力,通常以每秒处理的任务数量或者数据传输量来表示。
  3. **时钟周期:**几乎所有计算机都用时钟来驱动硬件中发生的各种事件,时钟间隔的时间称为时钟周期。
  4. 单位通常是纳秒(ns)、微秒(μs)或者毫秒(ms)。
  5. **时钟长度:**每个时钟周期持续的时间长度。
  6. 时钟频率(主频):即时钟周期的倒数,表示单位时间内时钟周期的数量,通常以赫兹(Hz)为单位。10Hz表示每秒10次。

CPU性能:CPU性能是一个核心指标。CPU执行时间,即程序在CPU上运行所需的时间,是评估CPU性能的关键。

==CPU执行时间 = CPU时钟周期数 × 时钟周期时间==

​ 时钟频率和时钟周期时间互为倒数关系:

==CPU执行时间 = CPU时钟周期数 / 时钟频率==

​ 硬件设计者可以通过减少程序的CPU时钟周期数或降低时钟周期时间来提高性能。然而,在实际的设计过程中,设计者经常需要在这些因素之间进行权衡。因为很多技术在减少时钟周期数的同时,可能会导致时钟周期时间的增加。

指令性能:CPI (clock cycle per instruction) 表示执行每条指令所需的时钟周期数的平均值

==CPU时钟周期数=程序的指令数 CPI==

==CPU时间 = 指令数 * CPI * 时钟周期时间==

==CPU时间 = (指令数 * CPI) / 主频==

​ 上式表明,CPU的性能(CPU执行时间)取决于三个要素:主频、CPI 和指令条数。主频、CPI和指令条数是相互制约的。例如,更改指令集可以减少程序所含的指令条数,但同时可能引起CPU结构的调整,从而可能会增加时钟周期的宽度(降低主频)。

​ 所以,在衡量一个计算机的好坏的时候,我们要把全部的三个因素全部都考虑进去才行。而不是只讨论其中一个:

image-20240416213506935

​ 有一种用 **MIPS **(million instructions per second, 每秒秒百万条指令)取代时间以度量性能的方法。对于一个个给定的程序, MIPS 表示为:

==MIPS=指令条数/(执行时间x10^6)=主频/(CPI * 10^6)。==

​ 但是 MIPS 规定了指令执行的速率,但没有考虑指令的能力,没有办法用 MIPS 比较不同指令集的计算机,因为指令数肯定是不同的。(考研中遇到,平时生活中或工作中不会用这个)

​ **功耗:**功耗代表了我们需要用多大的代价来进行冷却。

​ 在计算机设计中,价格、性能和能耗是三个关键因素。价格反映了计算机的成本和普及程度,性能则决定了计算机能够完成的任务和效率,而能耗则关系到计算机的续航能力和环境影响。最佳的设计应该根据特定的应用领域和需求,在这三个因素之间取得适当的平衡。

关键进制转化方面的问题

小时分钟秒---->60 秒毫秒微秒纳秒---->1000

IPS就是每秒执行多少指令

1B (byte,字节) = 8 bit (比特,又称"位", 计算机中每个0或1就是一个位(bit)) ; 1KiB(Kibibyte,千字节)=1024B= 2^10 B; 1MiB (Mebibyte,兆字节,百万字节,简称"兆") =1024KB= 2^20 B ; 1GiB (Gibibyte,吉字节,十亿字节,又称"千兆") =1024MB= 2^30 B ; 1TiB (Tebibyte,万亿字节,太字节) =1024GB= 2^40 B ; 1PiB (Pebibyte,千万亿字节,拍字节) =1024TB= 2^50 B ; 1EiB (Exbibyte,百亿亿字节,艾字节) =1024PB= 2^60 B ; 1ZiB (Zebibyte,十万亿亿字节,泽字节) = 1024EB= 2^70 B ; 1YiB (Yobibyte,一亿亿亿字节,尧字节) = 1024ZB= 2^80 B ;

简单来说 B->K->M->G->T.... 之间都是1024

在其他方面如Hz->KHz->MHz->Ghz之间都是1000

简单来说在储存方面简直时1024,但是在速率方面进制都是1000

# 第二章 指令系统

# 1.1 指令

机器指令(简称指令):指示计算机执行某种操作的命令,计算机语言中的基本单词。

​ 一台计算机的所有指令的集合构成该机的指令系统,也称指令集

​ 指令集的两种形式: 一种是人们编程书写的形式(汇编语言-助记符),另一种是计算机所能识别的形式(机器语言-二进制元) 。

​ 指令集有很多种:MIPS;Intel x86;ARM系统:ARMv7,ARMv8; RISC-V......此笔记以MIPS为例

存储程序:必须将事先编好的程序和原始数据送入主存后才能执行程序,一旦程序被启动执行,计算机能在不需操作人员干预下自动完成逐条取出指令并执行的任务。

​ 存储程序是1945年 冯. 诺伊曼提出的,“存储程序(stored-program)通用电子计算机方案”,宣告了现代计算机结构思想的诞生。

硬件设计的三条基本原则:

  1. 简单源于规整:这意味着设计应该尽可能地简单,以便于理解和维护。这通常通过保持设计的规整性来实现,例如通过保持指令长度的统一,可以简化处理器的设计和编程。
  2. 越小越快:在硬件设计中,减小组件的尺寸可以提高其运行速度。这是因为当信号传输的距离变短时,传输时间也会减少,从而提高了整体性能。
  3. 优秀的设计需要折中的方案:在实际的硬件设计中,往往需要在不同的设计要求之间做出权衡。例如,为了提高性能,可能需要增加功耗或牺牲一部分能效。因此,设计师需要找到一个能够满足所有关键要求的最佳折中方案。

​ 以上原则不仅适用于硬件设计,也可以作为指导任何复杂系统设计的原则。

# 1.2 计算机硬件的操作

​ 任何计算机必须能够执行算术运算,MIPS汇编语言用下述计法:

add a,b,c
1

​ 表示将两个变量 b和c相加,并将它们的和放入变量a中

​ 这种表达的方式是固定的每一条指令只用来执行一个操作。与加法类似的指令一般都有三个操作数:两个进行运算的数和一个保存结果的数。要求每条指令有且仅有三个操作数。体现了:简单源于规整

如果要进行四个数b,c,d,e相加,并将其储存在a中要用到如下指令:

add a,b,c
add a,a,d
add a,a,e  
1
2
3

这三条指令完成了四个指令的相加

# 1.3 计算机硬件的操作数

操作数:与高级语言程序不同, MIPS 算术运算指令的操作数是很严格的,它们必须来自寄存器

寄存器:由硬件直接构建且数量有限,是计算机硬件设计的基本元素。MIPS 体系结构中寄存器大小为 32 位,称其为字。

:计算机中的基本访问单位,通常是 32 位为一组,在 MIPS 体系结构中与寄存器大小相同。MIPS按字节编址,一个字对应4个字节。

寄存器的数量是有限的越小越快。大量的寄存器可能会使时钟周期变长,因为电信号传输更远的距离必然花费更长的时间。对于这32个寄存器,我们用0-31来表示。尽管可以简单使用序号0-31 表示相应的寄存器,但MIPS 约定书写指令时用一个符“$”后面跟两个字符来代表一个寄存器:$S0。

# 存储器操作数

​ 只能将少量数据保存在寄存器中,但存储器可以存储数十亿的数据元素。因此,数据结构是存放在存储器中的。

​ 寄存器的数量相对于程序可能需要存储的变量数量而言通常是非常有限的。将最常用的变量保存在寄存器中可以显著提高程序执行效率。

​ 当一个变量不再频繁使用或者当前需要用到的变量太多,而寄存器资源不足时,编译器会选择将某个寄存器中的变量“溢出”到内存中存储,这个过程称为寄存器溢出。

​ 现代编译器还会采用各种优化技术,如寄存器重用、局部优化、全局优化等策略,以最大程度地利用有限的寄存器资源,提高程序性能。

​ MIPS 的算术运算指令只对寄存器进行操作,因此,MIPS 必须包含在存储器和寄存器之间传送数据的指令。这些指令叫作数据传送指令

​ 为了访问存储器中的一个字,指令必须给出存储器地址。将数据从存储器复制到寄存器的数据传送指令通常叫**取数 (load) 指令。**MIPS取数指令助记符是lw。也就是load word的缩写:

# 基本语法:lw 目标寄存器, 偏移量(基址寄存器)
lw $t0, 100($s1)
1
2

​ 这条指令表示从寄存器 $s1 中的内容所指向的内存地址增加100个字节后的位置,加载一个32位的字数据到寄存器 $t0 中。在这里,"$s1" 是基址寄存器,"100" 是偏移量。

​ 和取数相对应的就是存数(sw):

# 基本语法:sw 源寄存器,偏移量(目的寄存器)
sw $s1, 4($s2)
1
2

​ MIPS按字节编址,一个地址空间存放一个字节。每个字都占据4个连续的字节位置。

​ 例如,如果一个字的地址是 0x1000 ,那么这个字所包含的四个字节的地址分别是:

第1个字节: 0x1000
第2个字节: 0x1001
第3个字节: 0x1002
第4个字节: 0x1003 
1
2
3
4

​ 因此,连续两个字在内存中的地址会相差4,因为每个字都占据4个连续的字节位置。下一个字的地址将是 0x1004 。

​ 所以,针对于MIPS来说,他是32位,以4字节作为一“字”。所以字 的起始地址必须是 4 的 倍数。这叫对齐限制 。这种对齐方式确保了CPU能够有效地从内存中读取和写入数据,且能加快数据传输。后面我们会详细介绍一这点。大部分的体系结构都具有这样的限制。

例如0x1003为字的首地址,这一地址是非法的

​ 有了编址,编写地址,也就要有寻址。也就是寻找地址。首先,既然编址按字来,那么寻址也是按字来。有两种类型的字节寻址的计算机:大端和小端。

  1. 大端(Big-endian):在大端编址方式中,一个多字节数据的高位字节存储在内存的低 地址处,而低位字节则存储在内存的高地址处。例如,一个32位的字(假设其值为 0x12345678)在内存中会被存储为:

    地址: 0x0000 0x12
    地址: 0x0001 0x34
    地址: 0x0002 0x56
    地址: 0x0003 0x78
    
    1
    2
    3
    4

    MIPS 架构就是使用大端编址的。

  2. 小端(Little-endian):在小端编址方式中,一个多字节数据的低位字节存储在内存的低地址处,而高位字节则存储在内存的高地址处。对于同样的32位字(0x12345678),它在内存中的存储方式会是:

    地址: 0x0000 0x78
    地址: 0x0001 0x56
    地址: 0x0002 0x34
    地址: 0x0003 0x12
    
    1
    2
    3
    4

    许多现代的PC和个人设备(如Intel和AMD的x86和x64架构)使用小端编址。

    ​ 例如:一个32位的字(0x12345678)用小端存放地址,字的首地址为0x0000,问0x0001的存放的数是多少?

    ​ 答:56

# 常数(立即数)

​ 程序中经常会在某个操作中使用到常数——例如,将数组的下标加 1 。

​ 从已介绍过的指令看,如果要使用常数必须先将其从存储器中取出。为了提高性能并减少能耗,们会有专门设计的指令来处理常数操作数:MIPS 架构的 addi 指令(加立即数)。

#基本语法:addi  目标寄存器  源寄存器  立即数 
addi $s3, $s3, 4
1
2

​ 这条指令的意思是将寄存器 $s3 的值加4

​ MIPS 架构中有一个特殊的寄存器,名为 $zero (编号为 0),它在硬件层面被永久设置为 0。这个寄存器的存在使得程序员在需要将某个寄存器清零或者将 0 赋值给其他寄存器时,可以直接使用$zero 寄存器作为源操作数,从而简化指令和提高代码效率。

# 将寄存器 $t1 清零:
addi $t1, $zero, $zero
# 给t1赋值成15:            
addi $t1, $zero, 15
1
2
3
4

​ 常数操作数出现频率高,而且相对于从存储器中取常数,包含常数的算术运算指令执行速度快很多,并且能耗较低。 根据使用频率来确定要定义的常数是加速大概率事件的一个例子

# 1.4 有符号数和无符号数

​ 所有信息都由二进制数位 (binary digit) 或位 (bit) 组成,因此二进制数运算基本单位是 bit, 取值可以是两种状态之一:高或低,开或关,真或假,1 或 0。

二进制数的重要性:

​ 简洁性:二进制只有两个数字(0和1),这使得它成为计算机内部信息存储和处理的理想选择。计算机中的所有逻辑操作,如与(AND)、或(OR)、非(NOT)、异或(XOR)等,都可以直接对应于二进制数的操作。

​ 直接映射:计算机内部的逻辑电路的开关状态可以直接映射到二进制数的0和1上。例如,一个开启的晶体管可以代表1,而一个关闭的晶体管可以代表0。

​ 易于扩展:二进制数的算术运算规则相对简单,可以直接扩展到更复杂的数制,如八进制(octal)和十六进(hexadecimal)。 内存效率:由于二进制数的表示方式非常紧凑,因此在存储和传输数据时非常高效。

​ 信息的最小单位是比特(bit),它只能表示两种状态:0 或 1。多个比特被组合在一起形成字节(byte),通常一个字节由8个比特组成。 1B=8b多个字节组成一个字。

​ 最低有效位 表示最右边的一位,最高有效位 表示最左边的一位。 MIPS 的字有 32 位 ,如果要存储1011。那就是:0000 0000 0000 0000 0000 0000 0000 1011(高位补0)

​ 计算机硬件设计使得其能够高效地执行基于二进制的加法、减法、乘法、除法以及其他逻辑操作,所有这些都直接对应于底层电路中的电子信号的变化。这些基础操作构成了现代计算机计算能力的核心。

# 整数进制的转化

  • 二进制转换为十进制:逐位乘以2的幂次方求和。
  • 十进制转换为二进制:除2取余法

​ 将十进制数转换为二进制数是一个相对直接的过程。下面是一个简单的步骤说明如何将十进制数转换为二进制数:

  1. 确定要转换的十进制数:首先,确定你想要转换的十进制数。例如,我们将转换十进制数 233 为二进制数。
  2. 连续除以2:将十进制数除以2,并记下余数。然后将商再次除以2,并再次记下余数。重复此过程,直到商为0。将余数从下到上排列,这就是二进制数的表示。 233 / 2 = 116 ... 1 (记下余数1) 116 / 2 = 58 ... 0 (记下余数0) 58 / 2 = 29 ... 0 (记下余数0) 29 / 2 = 14 ... 1 (记下余数1) 14 / 2 = 7 ... 0 (记下余数0) 7 / 2 = 3 ... 1 (记下余数1) 3 / 2 = 1 ... 1 (记下余数1) 1 / 2 = 0 ... 1 (记下余数1,这是最后一次)
  3. 组合余数:将记录的余数从下到上排列,得到二进制数。在这个例子中,二进制数是 11101001 。 因此,十进制数 233 的二进制表示是 11101001 。

​ 计算机硬件设计使得其能够高效地执行基于二进制的加法、减法、乘法、除法以及其他逻辑操作,所有这些都直接对应于底层电路中的电子信号的变化。这些基础操作构成了现代计算机计算能力的核心。

# 区分正数和负数(原码和补码)

原码

​ 为了表示正数和负数,计算机程序使用了一种称为“符号和幅值”(sign and magnitude)的表示法,即原码

​ 高位(通常是第一位或最左边的位)用作符号位,0表示正数,1表示负数。其余位表示数值的幅值(即绝对值)。

例如:占8位的二进制数:

9D: 0000 1001B

-9D:1000 1001B

​ 固定位数(n+1位)的原码范围:

[-(2n-1), (2n-1)]

注意,计算机内部是使用补码来进行存储数据的。而不是原码。所以,8位二进制计算机实际数据范围是-128~127。而不是看原码的-127~127

​ 0的原码表示有两种:0000 0000 和 1000 0000

​ 当运算的结果超出了该数据类型能够表示的范围时,就会发生溢出。(计算机内部一般用补码存储数据)

原码加减法:与十进制一样,考虑进位和借位

注:在原码加减运算中,对于两个不同符号数的加法(或同符号数的减法),先要比较两个数的绝对值大小,然后用绝对值大的数减去绝对值小的数,最后还要为结果选择合适的符号。

原码表示法的优缺点

  • 原码表示法的优点:原码表示的优点是,与真值的对应关系直观、方便,因此与真值的转换简单,并且用原码实现乘除运算比较简便。
  • 原码表示法的缺点:0的表示不唯一,给使用带来不便;加减运算比较复杂,要考虑溢出、负借位等等问题

补码

​ 为了简化硬件设计并解决这些问题,现代计算机更倾向于使用二进制补码(two's complement)的方式来表示有符号整数。二进制补码不仅解决了正负数的区分问题,还使得加减运算能够通过相同的硬件电路实现,同时有效地处理了溢出情况,并消除了正负零的问题。

补码表示正数和负数

  1. 正数:与其原码相同,即最高位(符号位)为0,剩余各位表示数值的绝对值。

例如,十进制正数5,在8位二进制补码中的表示为: 0000 0101

  1. 负数:通过取其绝对值的二进制反码(将所有1变为0,0变为1),然后再加1得到的。

    例如,十进制正数5,在8位二进制补码中的表示为: 1111 1011

    1. 首先找到+5的二进制表示: 0000 0101
    2. 反码操作: 将每一位取反,得到1111 1010
    3. 加1操作: 1111 1011 因此,-5在8位二进制补码中的表示为 1111 1011。

注:反码:

1.正数的反码与原码相同。

2.负数的反码是将对应正数(绝对值)的二进制码按位取反。

二进制补码表示的整数转换为十进制数

  1. 正数:正常的二进制转十进制的转换规则。

    例如,8位二进制补码 0000 1010 表示正数 10。

  2. 负数:先转换为对应原码(除符号位外,按位取反 再+1),首位是1表示负数,其余按照正常的二进制转十进制的转换规则。

    例如,8位二进制补码 1000 1010 表示负数-118

n+1位的二进制可以表示的范围:

[-(2n), (2n-1)]

其中,几个特殊数据的补码表示:

(1)[+0]~补~=[-0]~补~=000...000(含符号位共n+1个0),0的补码唯一。

(2)[-2n]~补~=100...000(n个0),n+1位补码表示的最小整数。

(3)[2n-1]~补~=011...111(n个1),n+1位补码表示的最大整数。

补码的优势

  • 简化加减运算:补码表示法的优点在于它使得加法和减法运算可以使用相同的硬件电路来实现。在补码表示法中,减法运算可以通过加上减数的补码来实现,从而简化了计算机的设计和实现过程。
  • 无额外的零表示:在补码表示法中,只有一个零,不存在正零和负零的区别。
  • 溢出处理:当两个数相加时,如果发生溢出(最高有效位产生进位),结果仍会落在有效范围内,保持循环不变性,如在8位系统中从最大正值+127加1会自动变成最小负值-128。

关于补码溢出的讨论:补码表示法的优点之一是它可以很容易地检测到溢出,并且对于加法运算,溢出只可能发生在正溢出(两个正数相加得到负数)或负溢出(两个负数相加得到正数)的情况下。

  • 正溢出:当两个正数相加的结果超过了正数的最大表示范围时,就会发生正溢出。

  • 负溢出:当两个负数相加的结果超过了负数的最小表示范围时,就会发生负溢出。

  • 检测溢出:在补码表示法中,可以通过检查加法运算中的进位来检测溢出。如果两个正数相加时最高位(符号位)产生了进位,或者两个负数相加时最高位没有产生进位,那么就可以确定发生了溢出。

  • 处理溢出:处理溢出的方法取决于特定的应用程序和编程环境。在某些情况下,程序员可能希望捕获溢出并采取相应的措施,如设置错误标志、抛出异常或采取其他错误处理机制。在其他情况下,溢出可能被视为未定义行为,可能导致不可预测的结果。

​ 因为补码表示法具有以上优势:目前大多数现代计算机系统在进行数值的存储和运算时,普遍采用二进制补码表示法。对于整数运算来说,补码是主导的表示法

​ 值得注意的是,浮点数在计算机内部通常不是以补码形式存储的,而是遵循IEEE 754标准,该标准使用一种不同的编码方案(符号位、指数和尾数)来表示浮点数,后续章节会提到。

符号扩展(Sign Extension)是一种将较短位数的二进制数转换成较长位数二进制数的方法,同时保持数的数值不变。

​ 在符号扩展中,我们观察原始数的最高有效位,即符号位,然后将其复制到新数的高位部分。

例如:

16位的二进制补码数 10000000 00000001拓展为32位:111111111 11111111 10000000 00000001

16位的二进制补码数 00000000 00000001拓展为32位: 00000000 00000000 00000000 00000001

注:

​ 这种方法之所以正确,是因为二进制补码表示的正数实际上在左侧有无限多个0,而负数在左侧有无限多个1,只是为了适应硬件的位数宽度,数的前导位被隐藏了,符号扩展只是简单地恢复了其中一部分。

MIPS 完整指令格式为

Op Rs Rt Rd Shamt Func

  • Op:指令操作码,用于标识指令的类型和功能。 6位
  • Rs:第一个源操作数寄存器号,用于存放第一个操作数。5位
  • Rt:第二个源操作数寄存器号,用于存放第二个操作数。 5位
  • Rd:目标操作数寄存器号,用于存放指令执行的结果。 5位
  • Shamt:位移量字段,用于指定移位指令的位移量。 5位
  • Func:指令功能码,用于选择Op操作中的具体函数。 6位

# 1.5 逻辑操作

位运算:位操作是对整数或其他二进制表示的数据的每个位进行单独操作

​ 虽然早期的计算机仅对整字进行操作,但人们很快就发现,对字中由若干位组成的字段甚至对单个位进行操作是很有用的,如:

  • 位级操作:在计算机硬件和软件层面支持对一个字中的单个或一组位进行操作,这对于实现诸如错误检测(如奇偶校验、CRC校验)、数据压缩、编码解码(如ASCII到EBCDIC转换)、权限控制(如访问控制位)以及硬件接口信号处理等任务至关重要。
  • 布尔逻辑:逻辑门电路的设计和数字逻辑的应用也要求能够对位进行AND、OR、NOT等逻辑操作,这在计算机内部的控制电路中是基础性的工作。
  • 打包和拆包:在通信协议、数据格式化等领域,经常需要将多个小的数据元素(比如单个字符或标志位)组合成一个较大的字,或者从一个字中提取特定的位字段,这些操作都依赖于位级操作指令。

​ 打包(Packing): 假设有一个结构体,包含两个8位的字符变量(在C语言中):

struct {
unsigned char a: 4; // 使用了4位
unsigned char b: 3; // 使用了3位
unsigned char c: 1; // 使用了1位
} bit_fields;
// 打包操作:将这三个独立的位字段组合到一个完整的字节中
bit_fields.a = 10; // 设置a为十进制的10,即二进制的1010
bit_fields.b = 5; // 设置b为十进制的5,即二进制的101
bit_fields.c = 1; // 设置c为1
// 此时,整个字节可能是这样的(具体取决于编译器的实现方式):
// 二进制表示:1010 101 1,这个字节包含了三个位字段的数据
1
2
3
4
5
6
7
8
9
10
11

​ 拆包(Unpacking): 反向的过程就是从一个字节中提取出特定的位字段:

unsigned char packed_byte = ...; // 假设这是上面打包得到的字节
// 拆包操作:从packed_byte中获取各个位字段的值
bit_fields.a = (packed_byte >> 4) & 0x0F; // 右移4位并按位与,得到a的值
bit_fields.b = (packed_byte >> 1) & 0x07; // 右移1位并按位与,得到b的值
bit_fields.c = packed_byte & 0x01; // 直接按位与,得到c的值
// 这样就从原始的packed_byte中分离出了三个独立的位字段信息
1
2
3
4
5
6

​ 以上例子展示了如何通过位操作对数据进行打包和拆包,这种技术常用于优化存储空间、处理网络协议数据包以及硬件接口通信等方面。

​ 因此,现代计算机的指令集架构中通常包含了丰富的位操作指令,允许程序员直接对数据的各个位 进行操作,极大地增强了程序设计的灵活性和效率。

按位与(AND):同1为1

作用:

  1. 保留某些位
  2. 清除某些位
  3. 检查标志位
  4. 网络地址和子网掩码计算
  5. 硬件接口控制
  6. 数据有效性校验
  1. 保留某些位: 当您需要从一个整数中只保留特定位置作。例如,假设有一个8位二进制数 x = 0b1011010b00001111 进行按位与运算,结果会仅保留原
  2. 清除某些位: 如果想清零某个整数中的特定位,可以用一个所有位都是0,只有要清零 位是1的掩码进行按位与运算。例如,要清除 x 的最高位,可以使用掩码 0b11111110 进行与运算。
  3. 检查标志位: 在编程中,常常利用按位与来检查变量中的某一位是否设置了特定标志。比如,一个状态字节可能有多个标志位表示不同的条件,通过与相应的位模式进行比较,可以确定这些条件是否成立。
  4. 网络地址和子网掩码计算: 在网络编程中,IP地址通常用32位二进制数表示,子网掩码用于确定网络部分和主机部分。将IP地址与子网掩码做按位与运算,可以得到网络地址,即同一网络段内所有设备共享的部分。
  5. 硬件接口控制: 许多硬件寄存器或端口通过特定位来控制功能或读取状态信息,程序员可以通过按位与操作读取或修改这些位字段。
  6. 数据有效性校验: 某些情况下,按位与可用于简单地校验数据的有效性,如奇偶校验,通过对一组位进行与操作,可以快速判断是否有奇数个位被设置为1。

按位或(OR):有1为1

作用:

  1. 设置标志位
  2. 合并数据
  3. 条件赋值
  4. 硬件接口控制
  1. 设置标志位:在处理状态字或其他包含多个标志的变量时,可以利用按位或来快速设置某个特定的标志位。例如,若要设置一个32位变量的第5位为1,可将其与掩码0b00100000 进行按位或运算。

  2. 合并数据:当需要合并两个独立的数据集但不关心彼此冲突的位时,可以用按位或操作将它们结合在一起。比如,存储用户权限时,不同的权限可以通过各自的位表示,多个权限通过按位或组合到一起形成完整的权限集合。

    例如,在计算机科学中,我们常常需要管理一组标志或属性,每个属性用一个单独的比特位来表示是否存在(1代表存在,0代表不存在)。

    假设你有一个用户权限系统,其中:

    权限A对应于第0位(即2^0=1)

    权限B对应于第1位(即2^1=2)

    权限C对应于第2位(即2^2=4)

    如果用户1拥有权限A和权限C,则其权限可以表示为:

    user1_permissions = 0b0101 # 这是一个二进制数,等价于十进制的5
    
    1

    另一个用户2可能只拥有权限B:

    user2_permissions = 0b0010 # 这是一个二进制数,等价于十进制的2
    
    1

    现在,如果你想合并这两个用户的权限,使得结果包含他们各自的所有权限,你可以使 用按位或运算符 | :

    merged_permissions = user1_permissions | user2_permissions
    
    1

    计算后得到:

    merged_permissions = 0b0111 # 这是一个二进制数,等价于十进制的7
    
    1

    在这个例子中,通过按位或运算,结果中的每一位都是原两个用户权限对应的位中有1的那个位置上的值。因此,合并后的权限包括了所有原始的权限位——权限A、权限B和权限C。这样,我们就完成了对两个数据集(这里指两个用户的权限集合)的合并操作。

  3. 条件赋值:可以用来实现条件性的修改整数值。例如,在某些场景下,可能希望仅当某个 条件成立时才改变某个位置的比特,这时可以先对原始值进行非条件的按位与运算清除目标位,然后与新值按位或起来完成条件赋值。

  4. 硬件接口控制:在与硬件通信时,设备寄存器通常有各种功能控制位,通过向这些寄存器写入 经过按位或运算后的值,可以同时开启或关闭多个硬件特性或模式。

按位异或(XOR):不同为1

作用:

  1. 不改变原始数据进行翻转特定位
  2. 交换两个变量的值(无需临时变量)
  3. 计算奇偶校验位
  1. 不改变原始数据进行翻转特定位:如果想更改一个变量的某个比特位而不影响其他位,可以将其与一个仅该位为1的掩码进行异或运算。例如,若要切换一个字节的最低位,可以使用 x ^=1 来实现。

  2. 交换两个变量的值(无需临时变量):异或运算的一个经典应用是无临时变量交换两个整数的值。假设我们有两个整数a和b,可以用下面的操作来互换它们的值: a = a ^ b; b = a ^ b; a =a ^ b; 在这个过程中,a首先记录了原a和b的异或结果,然后将这个结果再与b异或就得到了原来的a值,而此时的a经过两次异或后恢复到了原来的b值。

  3. 计算奇偶校验位:在数据传输和存储时,异或运算常用于生成奇偶校验位,比如单个位的奇校验或偶校验。所有数据位通过异或运算得到的结果即为校验位,当数据在传输过程中发生错误时,可以通过重新计算并比较校验位来检测错误。

非/取反(NOT):单个操作数的每位取反,0变为1,1变为0

作用:

  1. 反转二进制位
  2. 计算数值的补码形式
  3. 创建掩码
  4. 判断奇偶性
  1. 反转二进制位:按位非运算将操作数的每个二进制位都取反。例如,对于一个二进制数 0101 ,执行按位非后结果将是 1010

  2. 计算数值的补码形式:在大多数计算机系统中,负数以补码形式存储。对一个正数执行按位非操作会得到一个负数,这个负数是原正数减去1然后取反的结果;反之,对一个负数执行按位非操作,则大致上可以得到该负数加1之后对应的正数(但不是绝对准确,因为溢出问题可能导致不一致)。

  3. 创建掩码:如果想要设置整数的特定位,可以使用一个只有特定位置为1的位掩码,并将其与整数进行按位或操作。例如,要设置整数 x 的最低位,您可以使用 x|= 1; 。

    要清除整数的特定位,可以使用一个除了特定位为0外其他位都为1的位掩码,并将其与整数进行按位与操作。例如,要清除整数 x 的最低位,可以使用 x &= ~1; 或者 x &= 0xFE; (假设整数是8位)。

    在进行位级逻辑操作时,也可以用按位与常用于生成位掩码,用于清除或设置特定的位。比如想要清除一个整数的所有位(即将其设置为0),应该使用 x&= 0;

    总之,位掩码是通过按位逻辑操作(如按位与、按位或、按位非等)来设置、清除或翻转整数的特定位的一种有效方法。

  4. 判断奇偶性:对于一个整数,执行按位与操作 n & 1 可以判断它是否为奇数。而进一步使用按位非运算 !(n & 1) 则可以判断它是否为偶数。

左移(Left Shift):将一个数的所有位向左移动指定次数,空出的低位补零

作用:

  1. 乘以2的幂
  2. 设置标志位
  3. 优化内存访问
  1. 乘以2的幂:左移一位相当于将该数乘以2,因为每一位都在二进制下向高位移动了一位,并且低位补零。例如,对整数 x 执行 x << n 操作,等同于将 x 乘以2^n

  2. 设置标志位:在一些系统编程或者硬件编程中,左移可以用来快速设置一组位中的特定位置为1,比如配置寄存器或者设置权限标志。

  3. 优化内存访问:对于某些类型的数组或数据结构,左移可能用于根据索引快速定位元素,特别是在处理按固定字节宽度存储的数据时。

右移(Right Shift):将一个数的所有位向右移动指定次数,高位是符号位时,可能补符号位(算术右移),也可能补0(逻辑右移)

作用:

  1. 除以2的幂
  2. 快速除法和乘法
  3. 提取低阶位信息
  1. 除以2的幂:对于无符号整数,右移一位相当于将该数除以2(向下取整),因为每一位都 在二进制下向低位移动了一位,高位补零。例如,对整数 x 执行 x >> n 操作,等同于将 x 除以 2^n(不考虑小数部分)。

  2. 快速除法和乘法:当需要计算某个数与2的幂次方的商时,连续右移n位可以得到近似结果(对于正数且忽略小数部分)。对于负数,在一些系统中(如采用算术右移的系统),右移会根据符号位来填充空出的高位,这可用于保持有符号数的符号不变,实现快速除以2的幂。

  3. 提取低阶位信息:在处理特定类型的数据编码或者标志字段时,右移可用于读取或清除最低有效位上的信息。

逻辑操作(Logical Operation): 在更广义的范畴内,逻辑操作通常指的是布尔逻辑运算,包括与、或、非三种基本逻辑关系。在计算机编程中,这些逻辑操作既可以应用于位操作,也可以应用于更高层次的数据结构和变量(如条件判断语句中的真值)。例如,在C语言中, && 、 || 和 ! 运算符就是进行逻辑与、逻辑或和逻辑非操作。

在C语言中,逻辑操作主要用于布尔类型的变量或表达式。 逻辑与(Logical AND)

int x = 5, y = 10;
if (x > 0 && y > 0) {
printf("x 和 y 都是正数\n");
}
1
2
3
4

逻辑或(Logical OR)

int z = -1, w = 0;
if (z < 0 || w == 0) {
printf("z 是负数或者 w 等于零.\n");
}
1
2
3
4

逻辑非(Logical NOT)

int a = 0;
if (!a) {
printf("a 不为真(或零)\n");
}
1
2
3
4

​ 虽然逻辑操作和位操作有区别,但在处理二进制位级别的数据时,二者往往是交织使用的。例如,通过位与操作可以实现掩码(masking)和清除特定位的功能;位或操作可用于设置特定位;而异或操作在某些情况下可以用于交换两个变量的值,或者进行简单的错误校验等。

# 1.6 字符表示

​ 发明计算机是为了数字计算,不过计算机很快被用于商业方面的文字处理 。

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是一种早期广泛采用的字符编码标准,它使用7位二进制数(一个字节的最低7位)来表示128种不同的字符,包括英文大小写字母、数字、标点符号和其他控制字符。后来,为了满足更多的字符需求,出现了扩展ASCII和其他编码方案,如ISO-8859系列和Unicode等,它们可以利用8位或更多位来表示更广泛的字符集,包括非拉丁字母、特殊符号和表情符等。

​ 现代计算机系统普遍使用8位作为基本的数据单位——字节,并在此基础上通过组合多个字节形成更大的数据类型(如16位的短整型、32位的整型和64位的长整型),以适应各种复杂的应用场景。而如今在文本处理领域,Unicode编码已经成为主流标准,能够支持世界上几乎所有的书写系统和数千种字符。

字符通常被组合为字符数目可变的字符串。表示一个字符串的方式有三种选择:

  1. 长度前置:在字符串的第一个位置存储字符串的长度。这种方式允许直接通过第一个字节或几个字节快速获取字符串长度,不需要遍历整个字符串来计算长度。

  2. 附加长度变量:在字符串数据之后额外存储一个变量(通常是整数类型),用来记录字符串的实际长度。

  3. 使用特定字符作为结束符:如C语言中采用的方式,即在字符串的最后一个有效字符后面添加一个特殊的字符(通常是ASCII码值为0的空字符'\0')来标识字符串的结尾。

每种方法都有其优缺点,选择哪种方式取决于具体的应用场景、性能需求以及内存管理策略等因素。