《深入学习计算机系统(C语言)》学习进程之第二章信息的表示和处理

计算机有时候是无法精确表示现实世界中的数字的,一个是因为计算机表示数字所要用到的bit是有限的,另外一个原因则是有些数字本身无法用计算机的(二进制)表示法来存储,例如0.1、0.2等等。

在面向对象的编程语言中,C++本身是建立在C的基础之上的,它们使用完全相同的数字表示和运算,且C标准的设计允许多种实现方式,而Java标准在数据的格式和编码上是非常精确具体的。

2.1  信息存储

字节是最小的可寻址的存储器单位,而不是在存储器中访问单独的位。机器级程序将存储器视为一个非常大的字节数组,即虚拟存储器(第一章中有讲过,虚拟存储器包括内存和I/O设备,而I/O设备在抽象层次上称为文件)。存储器每个字节都有个地址,统称虚拟地址空间。

2.1.1  十六进制表示法

在C语言中,以0x和0X开头的数字为十六进制数。字符‘A’~‘F’可以大写或小写,甚至是大小写混合(同C语言一样,Java也用0x开头来表示十六进制数值,而八进制数用0开头(不过比较容易弄混淆),二进制数用0b开头。    ——资料来源:《Java核心技术卷1(中文第9版)》第33~34页)。

二进制数与十六进制数之间的转换技巧:若 x 是2的非负整数n次幂,则x的二进制表示就是1后面有 n 个0。一个十六进制数后面有多少个0,那转换后的二进制数后面的0的个数就相当于前者的4倍。综上所述,当n可以表示成i + 4j时(其中0 ≤ i ≤ 3),我们可以把x写成开头的十六进制数字为1(i = 0)、2(i = 1)、4(i = 2)、8(i = 3),后面跟着j个十六进制的0。

该技巧的解析:

i.我们都知道4个二进制位可以表示一个十六进制数位,即从(0000)2对应(0)16到(1111)2对应(F)16,如果(1111)2再加个1,即(10000)2,则该二进制数就无法用一个十六进制数位来表示了。

ii.根据我们的经验,一个二进制数中若最左边为1,而1的右边为n个0,那么这个数转换成十进制数就是2n,反之亦然。

iii.二进制转换成十六进制是将整个二进制数从右往左分组,4个二进制位为一组,从前面的叙述可知每组对应一个十六进制数位,故一个以1为开头、后面是n个0的二进制数,转换成十六进制数时,它的末尾就有j = n/4个0。至于最后的(0001)2(有i = 0个0)、(0010)2(有i = 1个0)、(0100)2(有i = 2个0)、(1000)2(有i = 3个0),将其转换成十六进制即可。

在Java上二进制、八进制、十进制和十六进制之间的转换通过java.lang.Integer下的valueOf、parseInt和toString方法来完成。   ——《Java核心技术卷1(原书第9版)》第189页

2.1.3  数据大小

字长是整数和指针数据的标称大小。

C语言中的数据类型所分配字节数依赖于机器和编译器。因此,用C语言编写的程序在可移植性上会受到限制。

2.1.4  寻址和字节顺序

当一个程序对象跨越多个字节时,我们需要考虑两个问题:这个对象的地址是什么,以及在存储器中如何排列这些字节。通常情况下,多字节对象在存储器中以连续的字节来排列,对象的地址为所使用字节中的最小地址。

注意理解本书第26页有关小端法和大端法的概念。

因为无论为哪种类型的机器编译的程序都能得到相同的结果,所以对大多数程序员来说,他们的机器所使用的字节顺序是完全不可见的。但字节顺序在某些情况下也会成为问题。首先是在不同类型的机器之间通过网络传送二进制数据时,接收方会得到与发送方截然相反的字节顺序。对此,在网络应用程序的编写中应遵守关于字节顺序的网络标准,接收方则可以将符合网络标准的数据转换为它的内部表示,以此作为折中方案。

与字节顺序有关的第二种情况是,我们在检查机器级程序中的字节序列时所用到的字节顺序也很重要。当阅读小端法机器生成的机器级程序时,经常要将字节按照相反的顺序来读取,这样得到的数字才与我们书写数字时最高有效位在左边,最低有效位在右边的方式相符。

注意本书第33~35页有关强制类型转换的描述及本人在上面所作的笔记。

C语言中字符串被编码为一个以null字符‘/0’作为字符串终止标志,而java则没有这个说法。

在使用ASCII码作为字符码的任何系统上都能得到相同的结果,与字节顺序和字大小无关,故文本数据比二进制数据有更强的平台独立性。

以C语言为例,将一个C函数在不同类型的机器上编译时,会产生不同的二进制机器代码。因为不同的机器类型使用不同的且不兼容的指令和编码方式,即使是同一进程在不同的操作系统上也会有不同的编码规则,故二进制代码是不兼容的。

关于整数运算中的截断问题:

无论是无符号数还是有符号数,它们之间的加法乘除基本运算都是建立在位级的表示基础上的,最终结果无论溢不溢出,在位模式上都会保持原有的位个数。

整数乘法:

整数乘法指令消耗的时钟周期比较多,而其他整数运算(例如加法、减法、位级运算和移位)只需要1个时钟周期。编译器为了对其优化,以移位、加法和减法的组合来代替常数因子的乘法。

当然,选择使用移位、加法和减法的组合,还是使用一条乘法指令,取决于这些指令的相对速度,而这些是与机器高度相关的。

整数除法:

整数除法要比整数乘法更慢一些。除以2的幂也可以用移位——右移来实现。无符号和补码数分别使用逻辑移位和算术移位来达到目的。

两个正整数(无论是无符号数还是有符号数)相除,结果都是正整数(将小数部分舍去)。这与位级的表示和移位有关。

当有负整数(有符号数)参与时,移位会导致向下舍入而不是向零舍入(这与我们的主观意识相反)。此时,可以在正常右移之前将小于0的被除数偏置(加上除数再减1),那么就可以得到正确的结果。

整数除法中正整数(无论是无符号数还是有符号数)的右移能产生与我们主观意为相符的结果,而负整数(有符号数)的右移却不能产生正确的结果的原因(请结合书上的2.3.7节来理解):

  • 一个正整数,它在右移后有可能把原来在位级上的非零位移到小数点后边去了,即把某些非零位通过右移(即除以2k)给移走了,而在计算机的整数运算中只可能显示出小数点左边的位模式。但在自然界中,非零数(即上面说的非零位)无论怎么除,结果都不会为零。比如1除以2、1除以22、1除以24、1除以216……,结果都不为零,只是精度能保留到多少而已。也就是说,正整数被除后,它的结果在小数点后面还可能会有非零的数的,但因为在计算机中整数运算的性质,这些非零数被抹去了,只留下小数点左边的整数,结果只能比原来的我们在小学学的除法运算的结果小,但符合我们主机意识中对计算机整数除法的理解,即向零舍入。
  • 负整数的右移和上述相同。右移把结果中小数点右边的非零位给抹去了。这些非零位其实是正的,但因为抹去了,结果就为原来的负数结果减去这些非零位所代表的值,那么,最终结果也就形成了向下舍入而不是向零舍入的这个局面。

浮点数:

表示浮点数及其运算的标准:IEEE标准754。

浮点数有规格化的、非规格化的、无穷大和NaN四种情况。

时间: 2024-10-10 11:44:37

《深入学习计算机系统(C语言)》学习进程之第二章信息的表示和处理的相关文章

iOS学习笔记---c语言学习第七天

结构体 结构体是一种自定义的数据类型 struct 结构体名 { 类型说明符  成员名: … 类型说明符  成员名: }: #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { struct teacher{ char name[30]; char sex; int age; char course[30]; }; typedef struct teacher Teacher; Teacher c

《深入理解计算机系统(第三版)》第二章 信息的表示和处理

<深入理解计算机系统(第三版)>第二章 信息的表示和处理 ??计算机本身是由处理器和存储器子通过系统组成.在核心部分,我们需要方法来表示基本数据类型,比如整数和实数运算的近似值.然后,我们考虑机器级指令如何操作这样的数据,以及编译器又如何将C程序翻译成这样的指令.接下来,研究几种实现处理器的方法,帮助我们更好的了解硬件资源如何被用来执行指令.理解了编译器和机器级代码,我们就能了解如何通过编写C程序以及编译它们来最大化程序的性能.本章主要使我了解了如何表示和执行应用程序,通过一些技巧,来写出安全

【C/C++学习】C++语言学习积累

1.命名空间 namespace cq{} using namespace cq; 2.使用继承 class Man: public Person { } 3.使用父类的方法 Man::Man(char*name) : Person(name, 12) { cout << "Man name is:" << this->name << " and age is:" <<this->age<< e

[学习总结] python语言学习总结 (一)

用py也很久了,很多东西只知道拿来用,并没有深究,感觉这样是不够的. 我决定写这么一篇总结,把很多遗忘的东西拾起来,把很多没搞懂的东西搞清楚. 1.eval函数 用法:eval(expression, globals=None, locals=None) 解释:将字符串str当成有效的表达式来求值并返回计算结果. 就是可以将字符串转换为表达式来运行,当然可以转换为某个函数执行. python是用命名空间来记录变量的轨迹的,命名空间是一个dictionary,键是变量名,值是变量值. 当一行代码要

学习《Javascript权威指南》的第二章笔记

1.Javascript区分大小写,但是HTML不区分大小写 2.JS会忽略标识之间的空格,多数情况下也会忽视换行符,所以要采用 整齐.一致的编码风格 3.//用作结尾的注释,/* 和 */可以当跨行的注释,但不能有嵌套的注 释 4.标识符必须以字母.下划线或美元符号开始(数字是不允许作为首字 符出现的),后续字符可以包括数字 为方便起见我们一般用ASCII字母和数字来表示 5.对于分号“:”如果一条语句是独占一行的就可以不用在后面加分号 注意:并不是在每一处换行时都要加上分号,只有在缺少了分号

《数据结构与算法Python语言描述》习题第二章第三题(python版)

ADT Rational: #定义有理数的抽象数据类型 Rational(self, int num, int den) #构造有理数num/den +(self, Rational r2) #求出本对象加r2的结果 -(self, Rational r2) #求出本对象减r2的结果 *(self, Rational r2) #求出本对象乘以r2的结果 /(self, Rational r2) #求出本对象除以r2的结果 num(self) #取出本对象的分子 den(self) #取出本对象的

深入理解计算机系统读书笔记之第二章信息的表示和处理

这一章读完,嗯,感觉怎么说呢? 就是读完了而已,没有想第一章那样,有具体的一些收获什么的.可能是没有很认真的阅读.读的很匆忙,有的内容很晦涩难懂............ 不管怎样还是在写一写自己的收获吧 这一章讲的是信息的表示和处理, 信息在系统中是怎样表示的呢?就是以字节来进行存储.信息就是位+上下文(第一章里面讲的) 具体的信息是怎样表示的? 带符号整数,不带符号整数,浮点数,等等. 1位运算 移位,向右移位 x>>k 分为两种形式 (1)逻辑上,向右移位就是在左端添加k个零[an-1,a

《时间序列分析及应用:R语言》读书笔记--第二章 基本概念

本章介绍时间序列中的基本概念.特别地,介绍随机过程.均值.方差.协方差函数.平稳过程和自相关函数等概念. 2.1时间序列与随机过程 关于随机过程的定义,本科上过相关课程,用的是<应用随机过程>清华林元烈老师的书.第1章第5节: 上面的定义比较清楚明白.按照本书上的说法,随机变量序列就是一个随机过程,换句话说,在每一个t时刻,所研究的量都是一个随机变量.随机过程完整的概率结构是由每个时刻的有限联合概率分布族决定的,幸运的是,联合分布中的大部分信息可以通过均值.方差和协方差等加以描述,而不用去直接

《数据结构与算法Python语言描述》习题第二章第二题(python版)

ADT Date: #定义日期对象的抽象数据类型 Date(self, int year, int month, int day) #构造表示year/month/day的对象 difference(self, Date d2) #求出self和d2的日期差 plus(self, int n) #计算出日期第self之后n天的日期 num_date(self, int year, int n) #计算year年第n天的日期 adjust(self, int n) #将日期d调整n天(n为带符号整