CSAPP第二章-信息的表示与处理-随手记

2023-05-16

仅作为学习《深入理解计算机系统》第二章时的笔记,仅记录对自己有启发的部分,不作为知识整理。(直接看电子书就可以了)。

因为这本书知识点非常多,所以我会抽时间多次阅读,本文也会经常更新。

原码和反码会有两个0:正0和负0

原码:用第一个位来表示正负,后面的位来表示数的大小
反码:用一个正数取反来表示这个正数的相反数

这两种表示法都会存在两个0:+0和-0。

而使用补码就只有一个0了。

补码表示的新理解

关于补码,为了计算某个正数的相反数,可以用过取反+1的方式计算得到负数的补码表示。但是还有另一种方式能够更好的理解补码。
如果用5位来表示一个数:

下标43210代表的十进制数
每个下标的数值-168421
二进制数10110113
二进制数211101-3
二进制数311111-1
二进制数30111115

可以看到其实如果按照补码的逻辑,当使用5个位来存储数字时,最高位第5位作为符号位,它的数值为-16,其他第1到4位的数值为1、2、4、8,然后再对二进制数的各个位,乘以其对应的数值,再累加,就能得到十进制数的大小。
比如二进制数1,其十进制数=0*(-16) + 1*8 + 1*4 + 0*2 + 1*1 = 13。
而负数二进制2,其十进制数=1*(-16) + 1*8 + 1*4 + 0*2 + 1*1 = -3。

我们现在已经知道-3的二进制补码表示是:11101,很容易能通过这个数值表得到正3的二进制补码表示是00011。
使用上面的“取反后+1”的公式,也可以将-3转为正3:~11101 + 1 = 00010 + 1 = 00011

另外,还有两个比较有意思的内容:

  1. 想获得某个二进制补码表示的最小的数,只需要让其符号位为1,其他位为0即可。
    比如在5位表示数的情况下,最小的数是-16,即10000。
  2. 想获取-1,只需要让所有位都为1即可,那么想获得-1,可以直接用0按位取反。

按位左移和右移

左移无所谓,左移几位就往右边补0即可。
右移有两种,一种是算数右移,就是右移之后在左边补的位为符号位的那个数字;另一种是逻辑右移,右移之后在左边补0

按位移动位的数量,系统默认帮你取模

来源:https://www.jianshu.com/p/304bfdda6b6a
控制硬件时,常涉及打开/关闭特定的位或查看他们的状态,一般都会使用到按位运算符技术。

一个面试题:

int a = 1, b = 32;

print("%d, %d", a<<b, 1<<32);

答案是 1,0

a << b 的结果是1,是因为运行时会将操作数b对32取模,然后在进行移位操作。

布尔代数

所谓布尔代数,就是按位与(&),或(|),非(~),异或(^)。
需要注意和强调的是(应该已经强调无数遍了),&和&&不一样,|和||不一样,~和负号-不一样。

比较有意思的是:

  1. 异或有一个性质是:a ^ a = 0, (a ^ b) ^ a = b(因为0 ^ b = b)
  2. (x | -x) >> 31 ,当x为0时依旧为0,当x不为0时,为-1

小端和大端

简单记的话,就记一个数 0x12345678
存储地址从小到大依次从左到右(有语病,但意思是那个意思)
大端存的是12 34 56 78
小端存的是78 56 34 12

C语言unsigned int,会导致问题

一般来说数组的长度是大于等于0的,所以在设计过程中,为了能多获得一位存储空间,有的人会设计使用unsigned int存储。在C语言有一个size_t类型,其定义就是long unsigned int
但在遍历过程中,有可能会出现肉眼难以察觉的bug。

正常的代码:

   
    int a[5] = { 1, 2, 3, 4, 5 };

    int cnt = 5;
      printf("show values\n");
    for (int j = 0;j < cnt;j++) {
        printf("a[%d]=%d\n", j, a[j]);
    }

    int i;
    printf("start loop\n");
    for (i = cnt -2;i >= 0;i--) {
        printf("in loop,%u %u\n", i, cnt);
        a[i] += a[i+1];
    }
    printf("end loop\n");

    printf("show values\n");
    for (int j = 0;j < cnt;j++) {
        printf("a[%d]=%d\n", j, a[j]);
    }

此段代码给出一个数组a,该数组有5个元素。然后进行的操作是从数组的后面往前累加,最终a数组的第一个元素是之前a数组各元素的总和。

其运行结果如下:

show values
a[0]=1
a[1]=2
a[2]=3
a[3]=4
a[4]=5
start loop
in loop,3 5
in loop,2 5
in loop,1 5
in loop,0 5
end loop
show values
a[0]=15
a[1]=14
a[2]=12
a[3]=9
a[4]=5

如果我们把代码“int i”改成“size_t i”会如何呢?
需要修改的错误代码:

   
    int a[5] = { 1, 2, 3, 4, 5 };

    int cnt = 5;
      printf("show values\n");
    for (int j = 0;j < cnt;j++) {
        printf("a[%d]=%d\n", j, a[j]);
    }

    size_t i;
    printf("start loop\n");
    for (i = cnt -2;i >= 0;i--) {
        printf("in loop,%u %u\n", i, cnt);
        a[i] += a[i+1];
    }
    printf("end loop\n");

    printf("show values\n");
    for (int j = 0;j < cnt;j++) {
        printf("a[%d]=%d\n", j, a[j]);
    }

输出结果:

show values
a[0]=1
a[1]=2
a[2]=3
a[3]=4
a[4]=5
start loop
in loop,3 5
in loop,2 5
in loop,1 5
in loop,0 5
in loop,4294967295 5
in loop,4294967294 5
Segmentation fault (core dumped)

根据文章https://blog.csdn.net/wang93IT/article/details/72782379所说:

有些时候我们在一段 C/C++ 代码的时候,由于对一个非法内存进行了操作,在程序运行的过程中,出现了“Segmentation fault (core dumped)”——段错误。

可以看到当i为0的时候,i–操作使i变成了一个特别大的数字,然后取a[i]时没有找到了非法内存,于是报错。
解决办法就是不要用size_t,也就是不要用无符号类型去作为下标索引。但如果a数组本身非常大呢?因为sizeof()函数的返回值类型就是size_t。

不要担心,在CSAPP课上,老教授给出了一种解决方案,虽然不是很符合正常逻辑:
修改后的正常代码:

   
    int a[5] = { 1, 2, 3, 4, 5 };

    int cnt = 5;
      printf("show values\n");
    for (int j = 0;j < cnt;j++) {
        printf("a[%d]=%d\n", j, a[j]);
    }

    size_t i;
    printf("start loop\n");
    for (i = cnt -2;i < cnt;i--) {
        printf("in loop,%u %u\n", i, cnt);
        a[i] += a[i+1];
    }
    printf("end loop\n");

    printf("show values\n");
    for (int j = 0;j < cnt;j++) {
        printf("a[%d]=%d\n", j, a[j]);
    }

仔细对比可以发现,在第二个for循环for (i = cnt -2;i < cnt;i--)中,其第二格判断条件从i >= 0改成了i < cnt

运行结果:

show values
a[0]=1
a[1]=2
a[2]=3
a[3]=4
a[4]=5
start loop
in loop,3 5
in loop,2 5
in loop,1 5
in loop,0 5
end loop
show values
a[0]=15
a[1]=14
a[2]=12
a[3]=9
a[4]=5

输出结果正常了。这是因为它借助了“无符号数减到(有符号数意义下的)负数时,会变成一个非常大的数”的性质。

为什么无符号的0再减一个正数就会变成很大的数呢?
以无符号0减去1为例:
假设有一个5位表示的数00000,如果这个数是二进制补码,则当00000减去1时,会得到11111。
11111在二进制补码中,指的是-1,但如果在无符号数中,它指的是UMax,也就是无符号数能表示的最大数。

二进制补码下,所有数字都有相反数吗?

不是的,TMin 100…000那个数字没有,也就是我们int下最小的数:-2147483648。
这个数字取反+1后会变成它自己,而不是正的2147483647。

但这也是为了在二进制补码中只有唯一的0作出的牺牲。

关于浮点数,非常形象的一张图

浮点数有五部分:

  1. 0
  2. 非规格化部分
  3. 规格化部分
  4. 无穷
  5. 不是一个正数(NaN)

在这里插入图片描述

图片来自《深入理解计算机系统》第三版第80页。

浮点数的“舍入”

因为浮点数有精度限制,所以在运算中,需要进行一定的舍入,从而用有限的位来表示最接近目标实数的浮点数。
IEEE标准要求使用“向偶数舍入”,不是我们常说的四舍五入,而是四舍六入,五向偶数舍入。
四舍六入下:
1.4 -> 1
1.6 -> 2

向偶数舍入下:
1.5 -> 2
-1.5 -> -2
因为1.5两边的数字是1和2,要向偶数舍入,所以取2;同理,-1.5两边的数字是-1和-2,向偶数舍入,取-2。
关于二进制向偶数舍入,书上描述为:

在这里插入图片描述

图片来自《深入理解计算机系统》第三版第84页。

之所以这么做,是因为如果全部向上舍入/向下舍入,会出现统计偏差。而向偶数/奇数舍入,则有50%的几率向上舍入,50%向下舍入,从而避免统计偏差。

关于溢出

不管是二进制补码、无符号数或者浮点数,都会存在溢出的情况。而溢出这个行为本身,C语言不会给出任何警告。所以只能通过良好的编程习惯和思维去避免。

溢出部分在计算机中会舍去。这带来了一些问题。比如加法和乘法,很有可能超过了Tmax或者Umax。

关于除法

除法运算是十分消耗资源和时间的。计算机发展到现在,除法依旧需要消耗大量的CPU时钟。
但由于我们存储整数使用的是二进制补码/无符号数,所以如果我们的除数是2的幂,则可以通过位移来进行除法运算。

当负数需要进行除2的幂的时候,需要加上偏移量,来保证舍入正确。

仅做记录,还未研究。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CSAPP第二章-信息的表示与处理-随手记 的相关文章

  • 《python+opencv实践》一、基于颜色的物体追踪(上)

    点击打开链接 本文主要参考国外一大牛博客 xff0c 然后自己修改得来 相关知识点在这里 实现功能 xff1a 追踪红颜色瓶盖 xff0c 并画出瓶盖轮廓和运动轨迹 from collections import deque import
  • C++的sort函数实现字符串排序

    一 背景 sort函数用于C 43 43 中 xff0c 对给定区间所有元素进行排序 头文件是 include lt algorithm gt 实现原理 xff1a sort并不是简单的快速排序 xff0c 它对普通的快速排序进行了优化 x
  • C# 中的Dispose()用法

    一 对Dispose方法的理解是什么呢 xff1f 使用Dispose方法的对象 xff0c 应释放它拥有的所有资源 它还应该通过调用其父类型的Dispose方法释放其基类型拥有的所有资源 net的对象使用一般分为三种情况 1 创建对象 2
  • C++的 remove函数

    一 介绍 remove函数原型如下 xff1a template lt class ForwardIt class T gt ForwardIt remove ForwardIt first ForwardIt last const T a
  • 主板上的南桥与北桥

    一 历史 曾经 xff0c 北桥芯片和南桥芯片都是主板芯片组中最重要的组成部分 传统来说 xff0c 靠上方的叫北桥 xff0c 靠下方的叫南桥 北桥负责与CPU通信 xff0c 并且连接高速设备 xff08 内存 显卡 xff09 xff
  • CMake的add_library与target_link_libraries

    一 add library介绍 使用该命令可以在Linux下生成 xff08 静态 动态 xff09 库so或者 a文件 xff0c Windows下就是dll与lib文件 xff0c 它有两种命令格式 1 1 第一种格式 xff1a No
  • Linux下终止正在执行的shell脚本

    一 问题 Linux系统Shell中提交了一个脚本 xff0c 但是需要停止这个进程 xff0c 如何处理 xff1f 二 方案1 killall fileName 说明 xff1a killall是一个命令 xff0c 不是kill al
  • Qt对象树的销毁

    一 问题 在C 43 43 中中 xff0c 我们都知道 xff1a delete 和 new 必须配对使用 一 一对应 xff1a delete少了 xff0c 则内存泄露 为什么Qt使用new来创建一个控件 xff0c 但是却没有使用d
  • DNS域名解析之递归与非递归查询

    DNS域名解析之递归与非递归查询 递归查询迭代查询实例 递归查询 主机向本地域名服务器的查询一般是递归查询 xff1a 如果本地域名服务器不知道查询的IP地址 xff0c 那么本地域名服务器就会以DNS客户的身份向根域名服务器继续发生请求
  • spi,iic,uart,pcie区别

    一 spi SPI 是英语Serial Peripheral interface的缩写 xff0c 顾名思义就是串行外围设备接口 xff0c 是同步传输协议 xff0c 特征是 xff1a 设备有主机 xff08 master xff09
  • 决策树的介绍

    一 介绍 决策树 decision tree 是一类常见的机器学习方法 它是一种树形结构 xff0c 其中每个内部节点表示一个属性上的判断 xff0c 每个分支代表一个判断结果的输出 xff0c 最后每个叶节点代表一种分类结果 例如 xff
  • 支持向量机

    一 是否线性可分的问题 考虑图6 1中 xff0c A D共4个方框中的数据点分布 xff0c 一个问题就是 xff0c 能否画出一条直线 xff0c 将圆形点和方形点分开呢 xff1f 比如图6 2中 xff0c 方框A中的两组数据 xf
  • cmake 链接库名称扩展

    多个文件 macro span class token punctuation span configure lib by types OUTLIBS DebugSuffix span class token punctuation spa
  • 如何自定义TCP通信协议

    物联网行业智能硬件之间的通信 异构系统之间的对接 中间件的研发 以及各种即时聊天软件等 xff0c 都会涉及自定义协议 为了满足不同的业务场景的需要 xff0c 应用层之间通信需要实现各种各样的网络协议 以异构系统的对接为例 在早期 xff
  • 使用米联客FPGA开发板 固化程序失败

    问题描述 xff1a 使用米联客FPGA ZYNQ7020开发板 xff0c 在利用工程和FSBL生成BOOT bin和fsbl elf文件 烧录FLASH时 xff0c 总是失败 这个问题折腾我小半天 xff0c xff0c 无语了 后来
  • Qt串口接收数据长度不稳定问题

    最近在做一个实时接收数据的项目 xff0c 需要每2ms接收下位机发来的两帧数据 xff0c 算是串口高速接收 在使用的过程中 xff0c 发现串口接收的数据长度不稳定 xff0c 有时长有时短 代码如下 xff1a connect ser
  • git的使用入门

    1 添加个人信息 git config global user name 名字 git config global user email 邮箱 git config global user phone 手机号 查看是否提交 git conf
  • Python-OpenCV之形态学转换

    目标 学习不同的形态学操作 xff0c 例如腐蚀 xff0c 膨胀 xff0c 开运算 xff0c 闭运算等 我们要学习的函数有 xff1a cv2 erode xff0c cv2 dilate xff0c cv2 morphologyEx
  • 在windows10系统中搭建mmdetection(2020.7.19)

    参考博客 https blog csdn net david lee13 article details 102940221 本人使用的版本 python 61 3 6cuda 61 10 0cudnn 61 7 5 1pytorch 61
  • C语言字节对齐详解

    C语言字节对齐12345 不同系统下的C语言类型长度 Data TypeILP32ILP64LP64LLP64char8888short16161616int32643232long32646432long long64646464poin

随机推荐

  • 深入学习卷积神经网络中卷积层和池化层的意义

    xff08 文章转载自 xff1a https www cnblogs com wj 1314 p 9593364 html xff09 为什么要使用卷积呢 xff1f 在传统的神经网络中 xff0c 比如多层感知机 xff08 MLP x
  • 关于LSTM的units参数

    LSTM units input shape 3 1 这里的units指的是cell的个数么 xff1f 如果是 xff0c 按照LSTM原理这些cell之间应该是无连接的 xff0c 那units的多少其意义是什么呢 xff0c 是不是相
  • C语言的queue函数

    转自 xff1a https blog csdn net zhang2622765758 article details 81709820 queue 模板类的定义在 lt queue gt 头文件中 与stack 模板类很相似 xff0c
  • pip/anaconda修改镜像源,加快python模块安装速度

    文章来源 xff1a https blog csdn net leviopku article details 80113021 修改镜像源的原因是pip和conda默认国外镜像源 xff0c 所以每次安装模块pip install 或者
  • Python-PackagesNotFoundError: The following packages are not available from current channels

    Python PackagesNotFoundError The following packages are not available from current channels 转载自 xff1a https blog csdn ne
  • Linux 下静态链接库.a 和动态链接库.so 的生成

    1 库 所谓的库就是一种可执行代码的二进制形式 xff0c 可以被操作系统载入内存执行 2 静态库和动态库 静态库 a 文件的命名方式 xff1a libxxx a 库名前加 lib xff0c 后缀是 a 库名是 xxx 链接时间 xff
  • CSDN如何转载别人的博客

    在参考 如何快速转载CSDN中的博客 后 xff0c 由于自己不懂html以及markdown相关知识 xff0c 所以花了一些时间来弄明白怎么转载博客 xff0c 以下为转载CSDN博客步骤和一些知识小笔记 参考博客原址 xff1a ht
  • 你有一条linux命令学习之解压缩.tar .gz .xz .bz .zip

    下载的包解压还是压缩本地的包 xff0c 都要用到解压缩命令 1 tar tar命令生成的压缩包 1 命令语法 tar xcfvzjJ pathname tar file 2 参数 c 创建包 x 解压包 v 显示解压缩过程 f 指定包名
  • raspberry pi 3 ModelB 更换内核、文件系统初探

    1 镜像烧录 1 下载官方最新镜像 xff1a https www raspberrypi org downloads 2 Win32DiskImager烧录 xff1a https sourceforge net projects win
  • char类型与int类型的相互转换、

    相关知识 xff1a 1 计算机中的一个unsigned char型数据表示0 255 xff0c 而一个signed char型数据表示 128 43 127 xff0c 都是256的数字 这256个数字 xff0c 在计算机的存储单元都
  • 使用printf输出各种格式的字符串

    xfeff xfeff 分类 xff1a 43 43 主题 使用printf输出各种格式的字符串 日期 2004 06 29 43 43 1 原样输出字符串 printf 34 s 34 str 2 输出指定长度的
  • float型变量和“零值”比较的方法

    前一段时间读了一下林锐博士的 高质量C C 43 43 编程指南 xff0c 其中有一个比较经典的问题 请写出float x与 零值 比较的if语句 xff1f 当时只知道不能直接用float类型的值与0进行 61 61 或 61 比较 x
  • 全局变量和局部变量

    全局变量也称为外部变量 xff0c 它是在函数外部定义的变量 它不属于哪一个函数 xff0c 它属于一个源程序文件 其作用域是整个源程序 在函数中使用全局变量 xff0c 一般应作全局变量说明 只有在函数内经过说明的全局变量才能使用 但是在
  • c 内存管理

    其他相关链接 xff1a https blog csdn net wind19 article details 5964090 一 几个基本概念 在C语言中 xff0c 关于内存管理的知识点比较多 xff0c 如函数 变量 作用域 指针等
  • Springboot操作MongoDB,包括增改查及复杂操作

    单条件查询 使用BasicDBObject配置查询条件 List span class token generics function span class token punctuation lt span AbstractMongoEn
  • 搭建Spark实战环境(3台linux虚拟机集群)(一)样板机的搭建

    系统及软件配置 系统配置 内存 xff1a 16g 2400 cpu xff1a i5 9400F 软件配置 Windows 10 1903版本VMware workstation 15 10CentOS centos release 7
  • 独立个人项目开发心得 - 任务切分、挑战性、实用性和半途而废

    在写文章前容许我啰嗦一下 xff1a 对于软件开发 xff0c 我走了不少弯路 xff0c 有时觉得自己作为API侠 xff0c 无所不能 xff0c 有时又觉得自己很多LeetCode题写不出来 xff0c 无能为力 我有一个博客 xff
  • 传统软件服务器与游戏服务器架构区别

    项目智能客服爬虫SLG游戏语言javapythonkotlin模型异步事件驱动可能没什么模型可言actor模型传输协议httphttptcp 43 netty传输结构jsonjsonprotobuf数据库oracle xff0c redis
  • Linux C++ Socket实战

    本文主要介绍Linux C 43 43 基础Socket网络编程 大部分知识来自于网站 xff1a https www geeksforgeeks org socket programming cc Socket编程状态图 从图中可以看到
  • CSAPP第二章-信息的表示与处理-随手记

    仅作为学习 深入理解计算机系统 第二章时的笔记 xff0c 仅记录对自己有启发的部分 xff0c 不作为知识整理 xff08 直接看电子书就可以了 xff09 因为这本书知识点非常多 xff0c 所以我会抽时间多次阅读 xff0c 本文也会