C++抽象编程——储存模式(1)——内存结构

来源:互联网 发布:免费已备案域名三级 编辑:程序博客网 时间:2024/06/05 07:19

在大多数情况下,我们所写的程序都依赖抽象数据类型来表示复合对象。从实践的角度来看,这个策略是极其常见的。当你以C++等面向对象语言编写程序时,应该尽可能利用库提供的抽象类型的优势,并尽可能远离底层细节的复杂性。因此了解C++如何表示数据是有用的。掌握这些知识可以让我们更好地了解这些抽象类型的工作原理,并帮助我们了解C ++的行为原理。
我们前面提到过的STL使编程变得更容易。在接下来的内容中,主要目标是了解如何有效地实现这些结构。评估不同可能算法的效率取决于了解这些策略如何利用内存以及各种选项涉及的成本。如果我们没有对底层结构的更详细的了解,这些是不可能评估的。

内存结构

在了解C ++的内存模式之前,你需要知道如何将信息存储在计算机中。每个现代计算机都包含一些高速内部存储器,它是主要的信息库。在典型的机器中,该存储器是由称为RAM的专用集成电路芯片构成的,代表随机存取存储器(In a typical machine, that memory is built out of a special integrated-circuit chip called a RAM, which stands for random-access memory.)。 随机访问存储器允许程序随时使用任何存储单元的内容。 RAM芯片运行的技术细节对大多数程序员来说并不重要。重要的是这些内存是怎么样组成的。

Bits, bytes, and words

在计算机内部,所有数据值,无论多么复杂,都作为基本信息单元的组合存储,这些基本单元被称为位(bit)。每个位可以处于两种可能的状态之一。如果你想到机器内部的电路,就像它是一个微小的灯开关,可以将这些状态标记为关闭和打开。如果您将每个位都视为一个布尔值,您可以使用标签false和true。由于字位原本来自二进制数字的收缩,所以更常见的是将这些状态标记为0和1,这是计算机运算所依赖的二进制数字系统中使用的两位数字。
由于单个位保存这么少的信息,因此个别位不是用于存储数据的最方便的机制。为了更容易地存储诸如数字或字符的这种传统类型的信息,我们将单独的位一起收集到更大的单元中,然后将其视为整体存储单元。最小的这样的组合单元称为一个字节(byte),由八位组成(1 byte = 8 bit),足够容纳char类型的值。在大多数机器上,字节被组装成的更加大结构,我们称为word,一个字,其中通常将单词定义为保持int类型所需的大小。 今天,大多数机器使用四或八个字节长(32或64位)的字。
特定计算机可用的内存量在很宽的范围内变化。早期的机器支持的内存大小以千字节(KB)为单位,80年代和90年代的机器的内存大小以兆字节(MB)为单位,而今天的机器通常以千兆字节(GB)测量内存体。然而,在计算机世界中,这些基数值不符合机器的内部结构。因此,按照传统,这些前缀被用来代表最接近他们传统解释的两个前缀的力量。 因此,在编程中,前缀kilo,mega和giga具有以下含义:

从70年代初的64KB计算机将有64×1024或65,536字节的内存。 同样,现代的4GB机器将具有4×1,037,741,824或4,294,967,296字节的内存。

二进制和十六进制表示

机器中的每个字节保存的数据,其含义取决于系统如何解释各个位。根据用于操作它的硬件指令,特定的位序列可以表示整数,字符或浮点值,每个都需要某种特定的编码方案。要描述的最简单的编码方案是对于无符号整数,其中这些位用于表示以二进制符号表示的整数,其中唯一的合法值为0和1,与底层位相同。 二进制符号在结构上与我们更熟悉的十进制符号相似,但使用2而不是10作为其基础。 二进制数字对整个数字的贡献取决于它在整个数字中的位置。最右边的数字表示单位字段,其他每个位置的数字是其右侧数字的两倍。
例如,考虑包含以下二进制数字的8位字节:

该位序列表示42,可以通过计算每个位的来验证,如下所示:

该图说明了如何将二进制符号转换为整数,但是这也说明以二进制形式编写数字是非常不方便的。二进制数是麻烦的,主要是因为它们往往很长。十进制表示是直观和熟悉的,但使得更难理解数字如何转换成位。
对于应用程序来说,了解数字如何转换为二进制表示形式是有用的,但是对于处理一整页的二进制数字,计算机科学家倾向于使用十六进制(16进制)表示。以十六进制表示法,有十六位数字,表示从0到15的值。十位数字0到9对于前十位数字是完全足够的,但经典算术没有定义你需要表示剩余六位数的额外符号。 计算机科学传统上使用字母A到F用于此目的,其中字母具有以下值:

使十六进制符号如此吸引人的是,你可以立即在十六进制值和底层二进制表示之间进行转换。所有你需要做的是将这些位数4个组成一组。 例如,42可以从二进制转换为十六进制,如下所示:

前四位表示数字2,下四个表示数字10.将其中的每一个转换为相应的十六进制数字,将其作为十六进制形式给出2A。然后,你可以通过将数字值相加来验证此数字仍然具有值42,如下所示:

在大多数情况下,为了数字的可读性。遵循使用下标来表示基数的策略。 因此,数字42,二进制和十六进制的三种最常见的表示如下所示:

其他数据类型的表示

在许多方面,现代计算背后的基本思想是任何数据值都可以表示为一组位。很容易看到,例如,如何在单个位中表示一个布尔值。所有你需要做的是将一个位的每个可能状态分配给两个布尔值之一。通常,0被解释为假(false),1被解释为真(true)。 你也可以通过将位序列解释为二进制符号的数字来存储无符号整数,以使8位序列00101010表示数字42.使用8位可以表示0之间的数字 和2^8 - 1或255.十六位足以表示0和2^16 - 1之间的数字或65,535。 32位允许高达2^32 - 1或4,294,967,295的数字

每个字节(8 bit)的内存可以存储0到255之间的数值,这意味着一个字节(byte)是存储ASCII字符的完美大小。其历史可追溯到其前身语言,C++将数据类型char定义为一个字节大小。 这种设计决策使得C ++程序更难以使用编入ASCII模型的语言编码所需的扩展字符集。 C++标准库定义了一个名为**wchar_**t的类型,以表示扩展到ASCII范围之外的“宽字符”。但是这些我们不做讨论。
整数也可以通过对编码进行较小的更改来存储为bit序列。主要是因为这样做简化了硬件设计,大多数计算机使用称为二进制补码运算(two’s complement arithmetic)的表示来表示有符号整数。如果要在二进制补码算术中表示非负值,则只需使用其传统的二进制扩展。要表示负值,你从2^N中减去其绝对值,其中N是表示中使用的位数。 例如,通过执行以下二进制减法来计算32位字中的* -1* 的二进制补码表示:

浮点数也表示为C++中的固定长度位序列。 虽然浮点表示的细节超出了我们讨论的范围,但是,假设构建硬件会使用一个字中的位的一些子集来表示浮点值中的数字,那么其他一些子集来表示该值被缩放的指数。 要记住的重要事情是,在内部,每个数据值被存储为位。
在C++中,不同的数据类型需要不同的内存量。对于原始类型,以下的值都是是典型的,尽管C ++标准为编译器编写者提供了一些灵活性,可以选择对特定类型硬件更为方便的不同大小:

在C++中,对象的大小通常只是它包含的实例变量的大小的总和。例如,我们之前定义的Point类,则其私有部分将包含以下实例变量:

int x;int y;

这些实例变量中的每一个通常需要四个字节,因此在大多数机器上存储该对象数据所需的总空间是八个字节。然而,编译器允许将内存空间添加到对象的底层表示,主要是因为这样做有时允许它们生成更有效的机器语言代码。因此,Point对象的大小必须至少为保存实例变量x和y所需的8个字节,但可能会更大
在C++程序中,我们可以使用 sizeof 运算符来确定向变量分配多少内存。sizeof运算符需要一个操作数,它必须是括号或表达式中的一个类型名称。如果操作数是一个类型,则sizeof操作符返回存储该类型的值所需的字节数,如果操作数是表达式,则sizeof返回存储该表达式的值所需的字节数。例如,表达式:

sizeof(int);

返回存储int类型所需的字节数。而表达式

sizeof(x);

则返回存储变量X所需的字节数。

原创粉丝点击