《Computer Systems a Programmer’s Perspective》,机械工业出版社。中文译名《深入理解计算机系统》。作者:(美)Randal E.Bryant / David O'Hallaron

想写点推荐理由,作罢,原因是自己学识尚浅(留坑以后写?)。这里附上几个链接,也许你看过之后,也会产生和我一样的想法,阅读这本计算机界的经典著作。

以下是正文


程序的结构和执行

这是本书的第一部分,Program Structure and Execution。包括内容是第二到第六章。第二章的标题是:信息的表示和处理(Representing and Manipulating Information)

位、字、数据类型

对于有十个手指头的人类来说,使用十进制表示法是很自然的事情,但是当构造处理信息的机器时,二进制的值工作得更好。……这些微不足道的二进制数字,或者成为位(bit),奠定了数字革命的基础

现代计算机的处理、存储的信息以二进制表示。甚至可以说计算机的一切就是0和1。一个bit就是一个0或1。那为什么就偏偏是二进制而不是我们常用的十进制,或者什么三进制五进制之类?简单来说还是离散信号和模拟信号的区别。简言之,0和1这种离散值在用机器来表示时是非常方便的,例如高低电平、纸条带上孔的有无等等。而且模拟信号(如电压)有诸如很容易受其他条件干扰,很容易产生误差这样的缺点,离散数值的优势就体现了。如果说,一个单独的位,只能代表0或1,起不了多大的作用。但如果有很多的位(足够数量),并且对二进制数字串进行一定形式的编码(例如IEEE浮点规则、ASCII码等等),那么我们能赋予一串二进制数一定的意义,让其具有存储、处理信息的能力。虽然0和1是离散的,但大量的0和1,足够让我们认为是“连续”的。此时,二进制数就会向我们展示它的魔力。

十进制0~15的三种进制表示

十六进制 十进制 二进制 十六进制 十进制 二进制
0 0 0000 8 8 1000
1 1 0001 9 9 1001
2 2 0010 A 10 1010
3 3 0011 B 11 1011
4 4 0100 C 12 1100
5 5 0101 D 13 1101
6 6 0110 E 14 1110
7 7 0111 F 15 1111
  • 这张表需要熟悉,也就是要了解进制转换的一些规则。

8个bit是一个字,或者叫做字节(byte)。字节是作为最小的可寻址存储器单位(并不是一个个位去存储和访问)。每个字节都有一个唯一的数字作为标识,这个数字就是地址(Address)

计算机有一个很重要的概念叫做字长(word size),指明整数和指针的标称大小。因为一个个地址都是唯一的数字,就是由一个字编码的。所以字长决定的一个最重要的系统参数就是虚拟地址空间的最大大小。一般来说32位的计算机,限定虚拟地址空间为4GB(2的32次方减1个字节)。

C语言数据类型的字节数

大端法、小端法

测试代码如下:

#include<stdio.h>
typedef unsigned char *byte_pointer; void show_bytes(byte_pointer start, int len) {
int i;
for (i = 0; i < len; i++)
printf(" %.2x", start[i]);
printf("\n");
} void show_int(int x) {
show_bytes((byte_pointer)&x, sizeof(int));
} int main()
{
int x = 12345;
show_int(x);
return 0;
}

在显示一个w位的整数时,如果w是8的整数,那么能以字节为单位来表示。最低有效字节排在前面的方式叫做小端法,大多intel兼容机器采取小端法。大端法采用的是最高有效字节在最前面,也就是我们平常习惯的方式。例如12345的32位编码是0000 3039。小端法是如下的输出。

bool代数、逻辑运算、移位运算

bool代数

位运算

以下是一些补充记录:

  • 掩码运算。按位与:&,常用于保持某位不变(x&1),或把某些位置为0(x&0)。按位或:|,常用于将某些位置置为1(x|1)。

  • 对于逻辑运算来说,0代表“false”,所有的非0值代表“true”。

  • 对于任何值a来说,a ^ a=0,因此有(a ^ b) ^ a=b ,加以利用可以带来有趣的结果。例如:a是密钥,b是明文,a^b是加密,加密后的值就被保护。想要知道明文,只能用a去解密

  • 对于右移,<<,机器支持两种模式的右移。

    • 逻辑右移,右移后在左端补0。
    • 算术右移,右移后在左端补最高有效位的值
    • 例如 对于 x = 0x10010011,x =<< 4; 如果是逻辑右移,结果为 x = 00001001;如果为算术右移,结果为11111001。
  • 移位数量应该保持小于字长。

  • 加减法优先级比移位高。

2017/03/14 update:关于位运算符放一张很久之前整理的表格:

整数(integer)表示

整数编码

整数分为有、无符号两种类型,(unsigned)。根据字节数的不同还分为char、short、int、long、long long。

对于整数的编码,简言之,就是每个位上面的值乘以该位对应的权值,再求和。

1.无符号数编码(binary to unsigned)

  • UMax,即编码全为1。
  • UMin,编码全为0。

2.补码编码(binary to Two’s compliment),与unsigned的区别在于最高有效位解释为负权

  • TMax,符号位为0,剩下位全为1。
  • TMin,符号位为1,剩下位全为0。
  • 求一个有符号数的负数(补码的逻辑运算非),可以理解为把全部位取反后,对结果再加1。特别地,TMin的“负数”依然是TMin。

C语言默认是有符号整型。如果想要无符号常数,需要加关键词U做限制。

无符号数和有符号数之间的转换

关键点:同样字长的有符号数和无符号数,使用的是相同的位模式。

难点:C语言中,对于一个运算,如果一个运算数是有符号的,另一个是无符号的,那么C语言会隐式地将有符号参数强制类型转换成无符号数,并且假设这两个数是非负的。因此,会导致一些奇怪的结果。例如:

对于第一个式子来说,由于一个操作数是有符号,另一个操作数是无符号,那么有符号数-1被强制转换成一个无符号数,会变成一个很大的无符号数。

第二个,-2147483647-1 其实就是头文件<limit.h>中32位补码TMin的写法(这是由于C语言规则所致)。转换成无符号之后的值要比TMax大1。

第三个,这两个操作数都是有符号型,而且2147483648大于TMax,导致溢出,成了一个很小的负数。

扩展、截断

#include<stdio.h>
typedef unsigned char *byte_pointer; void show_bytes(byte_pointer start, int len) {
int i;
for (i = 0; i < len; i++)
printf(" %.2x", start[i]);
printf("\n");
} void show_int(int x) {
show_bytes((byte_pointer)&x, sizeof(int));
} void show_short(short x) {
show_bytes((byte_pointer)&x, sizeof(short));
} int main()
{
//扩展
short x = 12345;
show_short(x);
printf("short x = %d\n", x);
show_int(x);
printf("int x = %d\n\n", (int)x); //截断
int y = 123456;
show_int(y);
printf("int y = %d\n", y);
show_short(y);
printf("short y = %d\n\n", (short)y);
return 0;
}

扩展,例如short到int,int到long long int这样。不会改变数字的值。如果是无符号数就是扩展全部为0的位,有符号数就是扩展全部为1的位。

截断,例如int到short,有点类似于右移,类似于mod 2的n次方,但只是类似。对于大的数据,截断会导致溢出。例如int型整数123456大于short能表示的最大数值范围,如果将其截断会导致上溢出从而得到一个负数。

2017/03/14 update:如果打算通过使用扩展位数的手段来使得一些运算结果由原本的溢出变为不溢出,那么强制类型转换和计算这两个过程的先后顺序很重要。例如下面这个函数验证两个32位的int型整数做乘法有没有产生溢出,验证的手段是先用64位的整型存结果,再将64位乘积截断为32位是否会改变其值:

int imull(int x, int y){
long long pll = (long long)x * y;
return pll == (int)pll;
}

代码第二行的强制类型转换至关重要,先转换再计算。如果第二行是这么写的话:

long long pll = x * y;

那么会用32位值来计算乘积(可能这会溢出),然后再将其扩展为64位。显然不会达到目的。可以参考下面的运行结果


See Also

部分公式、数据截图来源

【CSAPP笔记】1. 位、字节、整型的更多相关文章

  1. 面试问题2:给一个5G的大文件,保存的数据为32位的整型,找到所有出现次数超过两次的数字

    问题描述:给一个5G的大文件,保存的数据为32位的整型,找到所有出现次数超过两次的数字 大数据操作: 解决方法一: 依次遍历文件数据, 开始32二进制清0 每次读取一个数,先和二进制位与,如果为0 则 ...

  2. 编译器是如何实现32位整型的常量整数除法优化的?[C/C++]

    引子 在我之前的一篇文章[ ThoughtWorks代码挑战——FizzBuzzWhizz游戏 通用高速版(C/C++ & C#) ]里曾经提到过编译器在处理除数为常数的除法时,是有优化的,今 ...

  3. C/C++的64位整型

    在C/C++中,64为整型一直是一种没有确定规范的数据类型.现今主流的编译器中,对64为整型的支持也是标准不一,形态各异.一般来说,64位整型的定义方式有long long和__int64两种(VC还 ...

  4. C++64位整型

    今天在Ubuntu下编译C++代码,然后毫无防备的出现以下错误: 查阅了相关资料,__int64是VC++独有的,因此64位g++无法识别. 以下内容转载自:Byvoid 在C/C++中,64位整型一 ...

  5. C++将整型数据转换成大端或小端存储顺序

    大端和小端的概念参考之前博客: 大端/小端,高字节/低字节,高地址/低地址,移位运算 昨晚帮导师从指令中恢复图像的时候,导师要我转换成raw格式,也就是记录图像像素的二进制序列,然后反复强调让我注意大 ...

  6. GO语言学习——基本数据类型——整型、浮点型、复数、布尔值、fmt占位符

    基本数据类型 整型 整型分为以下两个大类: 按长度分为:int8.int16.int32.int64 对应的无符号整型:uint8.uint16.uint32.uint64 其中,uint8就是我们熟 ...

  7. 【CSAPP笔记】2. 整型运算

    现在想补补推荐这本书的理由. Most books on systems-computer architecture, compilers, operating systems, and networ ...

  8. Linux漏洞分析入门笔记-CVE_2018_6323_整型溢出

    操作系统   Ubuntu 16.04 /32 位 调试器     IDA pro 7.0 漏洞软件   binutils-2.29.1 0x00: 漏洞描述 1.什么是整数溢出: 在计算机中,整数分 ...

  9. Python入门篇-基础数据类型之整型(int),字符串(str),字节(bytes),列表(list)和切片(slice)

    Python入门篇-基础数据类型之整型(int),字符串(str),字节(bytes),列表(list)和切片(slice) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Py ...

随机推荐

  1. 用NI的数据采集卡实现简单电子测试之3——绘制二极管V-I特性曲线图

    本文从本人的163博客搬迁至此. 接下来用USB-6009和LabVIEW实现对二极管最重要的特性曲线“V-I特性曲线”的测试和绘制. 一.什么是二极管V-I特性曲线    康华光版的<电子技术 ...

  2. matlab2016b配置libsvm的各中坑及解决办法

    Q1:matlab2016b不能自动关联m文件! A1: (1)首先准备好工具,工具链接:pan.baidu.com/s/1t_KaFZNOFln9m57sMBTrkQ:提取码:x49w. (2)下载 ...

  3. tkinter的GUI设计:界面与逻辑分离(一)-- 初次见面

    PyQt实现界面与逻辑分离的设计很是方便,详情可以见我的第一篇博文. 不过本文将使用python的标准库 tkinter,来实现界面与逻辑分离的GUI 设计. 我们来设计一个很简单的程序: 目的:长度 ...

  4. windows7下解决caffe check failed registry.count(type) == 1(0 vs. 1) unknown layer type问题

    在Windows7下调用vs2013生成的Caffe静态库时经常会提示Check failed: registry.count(type) == 1 (0 vs. 1) Unknown layer t ...

  5. 【BZOJ1054】[HAOI2008]移动玩具

    [BZOJ1054][HAOI2008]移动玩具 题面 bzoj 洛谷 题解 太\(sb\)了,不想写了,直接点开洛谷题面单击右边蓝色按钮题解即可

  6. Linux下通过进程名查询占用的端口

    1.首先根据名称用ps命令查看进程ID: ps -ef | grep zookeeper jim 10997 1959 0 12月14 pts/2 00:00:01 /usr/lib/jvm/java ...

  7. 微信小程序充值及充值回调后的处理

    微信小程序的充值流程与 H5 或 公众号大致差不多,这里简单说一下前端在充值时候的一些操作流程. 用户在小程序中发起充值请求时,一般会先请求自己的服务器,将充值的参数发送给后端,然后后端会去请求微信充 ...

  8. Jmeter接口测试(五)变量及参数化

    在请求过程中,有时我们需要在请求中设置一些变量来测试不同的场景. 提示:在调试请求过程中,无关的请求可以暂时禁用掉,选择某个暂时不用的请求,右键--禁用 Jmeter 支持以下类型变量:所有类型的变量 ...

  9. 一切的浮点型进行计算操作都要用BigDecimal

    简化: 1.引言 float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的.然而,它们没有提供完全精确 ...

  10. PHP核心技术——反射

    反射: 反射指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类.方法.属性.参数等的详细信息,包括注释.这种动态获取信息以及动态调用对象方法的功能称为反射API class person{ ...