2.Representing and Manipulating Information

本章从二进制、字长、字节序,一直讲到布尔代数、位运算,最后无符号、有符号整数、浮点数的表示和运算。诚然有些地方的数学证明有些枯燥,但总体上看,本章还是干货十足的!

2.1 Decimal vs. Binary Notation

我们习惯十进制只是因为我们有十根手指头(?),所以会对二进制感到不习惯。但是二值信号(two-value signal)在表示、存储、传输方面有巨大优势,从打孔带上的有没有孔洞(代码的表示),到电线上的高低电压(数据的传输),到磁畴(magnetic domain)的顺时针、逆时针旋转(磁盘上存储)

2.2 Words

字长是CPU一次能够处理的数据长度,一般代表着能处理的数字长度,也代表了CPU数据通路的宽度(地址总线、数据总线)。

2.3 Addressing & Byte Ordering

字节序,大尾端小尾端,这些耳熟能详的词汇,仿佛已经翻来覆去学了好多回都要烂熟如心了。可这东西到底有什么用啊?答案是用处大着呢:
  • 不同机器间的二进制数据传输:不知道是大尾端还是小尾端的话,解析时肯定是错啊!
  • 反编译程序中的整数数据:指令倒无所谓什么顺序,但指令后面跟着的操作数要是不知道顺序那可就乱套了!例如反编译出程序中的一句:01 05 64 94 04 08。其中01 05表示add %eax寄存器,那么后面操作数64 94 04 08的字节序就至关重要了。小尾端的话,它就表示0x8049464,大尾端则完全相反。
  • 避开类型系统直接访问底层字节:例如C语言中用cast将对象转换成与其创建时完全不同的类型。应用编程当然不推荐这样写,但是在系统编程中这却非常有用!
下面就再再温习一下字节序吧。假设地址0x100处存放了一个4字节的整数0x01234567,即从地址0x100到0x103。那么大小尾端的存放方式如下图所示。一定要注意的是:所谓大小都是相对内存地址来说的。大尾端就是从低地址到高地址,低地址存高位,高地址存低位,所以叫大尾端。如果地址是从左向右增长的话,那么大尾端就与我们习惯的书写方式的顺序相同。


但是,对于字符串来说则不存在这种区分。例如字符串"12345"的字节为:31 32 33 34 35 00(终止符)。在任何使用ASCII作为字符编码的机器上,不论字节序、字长等环境如何,结果都是这样。因为字符串都是由一个个字符组成的,对于单个字符来说是不存在顺序的。所以说,字符数据比二进制数据具有更好的平台独立性。

扩展一下:那用多字节表示字符的Unicode编码有没有这种问题呢?
“UTF-8以单字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?Unicode规范中推荐的标记字节顺序的方法是BOM,Byte Order Mark。BOM是一个有点小聪明的想法:在UCS编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符”ZERO WIDTH NO-BREAK SPACE”。这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符”ZERO WIDTH NO-BREAK SPACE”又被称作BOM。

关于尾端的典故也很有意思,摘抄一段放松一下:
“Lilliput和Blefuscu这两大强国在过去的三十六个月里一直在苦战。战争开始时由于一下原因:我们大家都认为,吃鸡蛋前,原始的方法是打破鸡蛋较大的一端,可是当今皇帝的祖父小时候吃鸡蛋,一次按古法打鸡蛋时碰巧将一个手指弄破了,因此他的父亲,当时的皇帝,就下了一道赦令,命令全体臣民吃鸡蛋是打破鸡蛋较小的一端,违令者重罚。老百姓们对这项命令极为反感。历史告诉我们,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。这些判断大多都是由Blefuscu的王国大臣们煽动起来的。叛乱平息后,流亡的人总是逃到那个帝国区寻求避难。据统计,先后几次有一万一千人情愿受死也不肯去打破鸡蛋较小的一端。关于这一争端,曾出版过几百本大部著作,不过大端派的书一直是受禁的,法律也规定该派的任何人不得做官。"(摘自蒋剑锋译的《格利佛游记》第一卷第4章)Swift是在讽刺英国(Lilliput)和法国(Blefuscu)之间的持续的冲突。Danny Cohen,一位网络协议的早期开创者,第一次使用这两个术语来指代字节顺序。

2.4 Bit-level Operation & Logical Operation

香农创建了信息论,第一次在布尔代数和数字逻辑之间建立起了联系。
我们可以将布尔运算扩展到位向量上,那么位向量就有两个很有用的方式:
1)表示有限集合从而达到压缩的目的。我可以这样来编码集合A {0, 1, ... w-1}的任意子集,用位向量[aw-1, ..., a1, a0]。当i属于A时,将ai设为1。例如,我们用[01101001]表示[0, 3, 5, 6],用[01010101]表示[0, 2, 4, 6]。也就是说位向量是“倒着”保存的!(待深入研究)

2)信号掩码。不同信号能中断程序的执行,所以我们可以用特定的位向量来启用或禁用不同的信号。

经典的位运算:
1)清除值:x & 0xFF(只保留最低的一字节)
2)设置值:x | 0x3
3)可移植的“1”:~0
4)反转值:x ^ 0xF(异或的特性1:101 ^ 111 = 010,而特性2:101 ^ 000 = 101)
5)清零:XOR %edx, %edx (异或的特性3。为什么不直接设值,因为这种方式生成的机器码只有两字节,而设值有五字节)

逻辑运算与上面讲过的位运算经常是搞混。二者还是有很大的不同的:
1)逻辑运算将任何非0参数看做TRUE,并且只返回1或者0。因此只有当参数是0和1时,逻辑运算才和位运算有着相同行为。
2)逻辑运算具有短路的效果。当某一部分逻辑运算后已经能确定整个表达式的真假时,后面部分的逻辑运算就不会执行了。这种行为也是位运算所不具有的。

2.5 Integer Representation

对于无符号整数就非常简单了,每一位都表示2的i次方就可以了。


对于有符号整数呢,先说一种我们直接能想到的,用二进制表示整数的方式:用最高位当符号位,0表示整数,1表示负数。但这种方式有个重要的缺陷:整数0有正负两种表示方式。现代计算机使用的都是另一种我们耳熟能详的二进制表示方式:补码(two's complement)!最高位是1时则最高位的值是其权值取负。所以我们看汇编或机器码时经常能看到0xFFFF...xx,就是因为最高位是权值的负数,所以要置很多个次高位为1来表示一个“小”负数。例如0xFFFFFEC8=-312。小尾端机器上也就是C8 FE FF FF。

六星经典CSAPP笔记(2)信息的操作和表示的更多相关文章

  1. 六星经典CSAPP笔记系列 - 作者:西代零零发

    六星经典CSAPP笔记(1)计算机系统巡游 六星经典CSAPP笔记(2)信息的操作和表示 六星经典CSAPP-笔记(3)程序的机器级表示

  2. 六星经典CSAPP笔记(1)计算机系统巡游

    CSAPP即<Computer System: A Programmer Perspective>的简称,中文名为<深入理解计算机系统>.相信很多程序员都拜读过,之前买的旧版没 ...

  3. 六星经典CSAPP-笔记(7)加载与链接(上)

    六星经典CSAPP-笔记(7)加载与链接 1.对象文件(Object File) 1.1 文件类型 对象文件有三种形式: 可重定位对象文件(Relocatable object file):包含二进制 ...

  4. 六星经典CSAPP-笔记(11)网络编程

    六星经典CSAPP-笔记(11)网络编程 参照<深入理解计算机系统>简单学习了下Unix/Linux的网络编程基础知识,进一步深入学习Linux网络编程和TCP/IP协议还得参考Steve ...

  5. 六星经典CSAPP-笔记(10)系统IO

    六星经典CSAPP-笔记(10)系统I/O 1.Unix I/O 所有语言的运行时系统都提供了高抽象层次的I/O操作函数.例如,ANSI C在标准I/O库中提供了诸如printf和scanf等I/O缓 ...

  6. 六星经典CSAPP-笔记(12)并发编程(上)

    六星经典CSAPP-笔记(12)并发编程(上) 1.并发(Concurrency) 我们经常在不知不觉间就说到或使用并发,但从未深入思考并发.我们经常能"遇见"并发,因为并发不仅仅 ...

  7. 六星经典CSAPP-笔记(3)程序的机器级表示

    1.前言 IA32机器码以及汇编代码都与原始的C代码有很大不同,因为一些状态对于C程序员来说是隐藏的.例如包含下一条要执行代码的内存位置的程序指针(program counter or PC)以及8个 ...

  8. Java 接口与继承 道至简第六章发表阅读笔记

    一.继承条件下的构造方法调用 class Grandparent { public Grandparent() { System.out.println("GrandParent Creat ...

  9. 《C++Primer》第五版习题答案--第六章【学习笔记】

    <C++Primer>第五版习题答案--第六章[学习笔记] ps:答案是个人在学习过程中书写,可能存在错漏之处,仅作参考. 作者:cosefy Date: 2020/1/16 第六章:函数 ...

随机推荐

  1. 洛谷mNOIP模拟赛Day1-斐波那契

    题目背景 大样例下发链接:http://pan.baidu.com/s/1c0LbQ2 密码:jigg 题目描述 小 C 养了一些很可爱的兔子. 有一天,小 C 突然发现兔子们都是严格按照伟大的数学家 ...

  2. bzoj 3998: [TJOI2015]弦论

    Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个. ...

  3. hibernate4整合spring3出现java.lang.NoClassDefFoundError: [Lorg/hibernate/engine/FilterDefinition;

    解决办法 原先:<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annota ...

  4. Linux 在线模拟器

    最近在学习Linux的一些命令的使用,但是很久之前装的Linux虚拟机被删掉了,又不想为了练习几个命令折腾一遍虚拟机.所以,就尝试地搜了一下,看看有没有在线的Linux模拟器可以使用,只要可以练习一下 ...

  5. git报错:'fatal:remote origin already exists'怎么处理?附上git常用操作以及说明。

    git添加远程库的时候有可能出现如下的错误, 怎么解决? 只要两步: 1.先删除 $ git remote rm origin 2.再次执行添加就可以了. ---------------------- ...

  6. java怎样获取CPU占用率和硬盘占用率

    通过jmx可以监控vm内存使用,系统内存使用等,以下是网上某博客代码,特点是通过window和linux命令获得CPU使用率. 利用java程序实现获取计算机cpu利用率和内存使用信息. packag ...

  7. Microsoft Visual Studio 2017 编译最新版 libuv 1.x 并且生成 LIB 和 DLL 两种模式

    以为昨天晚上编译通过就可以了,哪知道,早上编译DLL的一车的报错 今天开始逐个解决,终于把引用的问题一亿解决了,具体步骤如下 1 在 Windows 平台下编译出错,显示导出未定义,打开 uv-win ...

  8. Linux下查看进程打开的文件句柄数

    ---查看系统默认的最大文件句柄数,系统默认是1024 # ulimit -n ----查看当前进程打开了多少句柄数 # lsof -n|awk '{print $2}'|sort|uniq -c|s ...

  9. popen() 使用举例 (转载)

    函数原型: #include "stdio.h" FILE *popen( const char* command, const char* mode ) 参数说明: comman ...

  10. 【if...else】三角形判断

    给定三条边的长度,判断能否组成三角形,如果可以,判断三角形的形状. 输入要求 一组数据,每行三个实数,在(0,10]之间 输出要求 根据每行的数据判断,如果不能组成三角形,则输出"Not a ...