CSAPP Lecture 02 - Bits ,Bytes and Integers
Lecture 02 - Bits ,Bytes and Integers
视频地址:https://www.bilibili.com/video/BV1iW411d7hd?p=2
课程地址:https://www.jianguoyun.com/p/DecAyVEQjqmrChiahrEE
思维导图:https://www.jianguoyun.com/p/DclOTz8QjqmrChi_hrEE
1. Bit
信息为什么用位来表示?
-
易于存储和传输
-
易于转化成其他进制
bit级别的操控
-
布尔代数(& | ^ ~)
-
逻辑运算(&& || !)
-
shift(<< >>)
2. Integers
有符号数和无符号数的表示
-
无符号数:
- 公式:\(B 2 U(X)=\sum_{i=0}^{w-1} x_{i} \cdot 2^{i}\)
- 取值范围(000...0,111...1)
-
有符号数:
-
公式:\(B 2 T(X)=-x_{w-1} \cdot 2^{w-1}+\sum_{i=0}^{w-2} x_{i} \cdot 2^{i}\)
-
取值范围(100...0,011...1)
-
最大值比最小值的绝对值少1
-
4bit无符号数与有符号数的位表示:
-
![]()
-
无符号数与有符号数之间的转换
- 根本原则:维持符号位不变,但重新释义
- 实际效果:正数部分不变,负数加减2^w
整数运算
-
加法:
-
无符号数:超限部分会被截短
- \(s=\operatorname{UAdd}_{w}(u, v)=u+v \bmod 2^{w}\)
-
-
有符号数:超限部分会被重新释义
-
-
-
乘法
-
无符号数
\(\text { UMult }_{w}(u, v)=u \cdot v \bmod 2^{w}\)
-
有符号数:除了符号位以外,和无符号数一样,
-
-
使用unsigned的合适时机
-
循环里面最好不要使用
-
unsigned 可能出的问题:0-1=Umax
用在循环里面的时候要特别注意- 错误的写法:
-
正确的写法:
-
-
在进行取模运算,shift运算的时候可以使用
-
3. 数据在内存中的存储形式
大端模式与小端模式
-
大端:最低字节具有高地址
- 代表机器:Sun,PPC MAC,Internet
-
-
小端:最低有效字节有低地址
-
代表机器:x86,ARM,IOS, Windows
-
-
程序:打印数据在内存中的存储形式
#include <iostream>
using namespace std;
typedef unsigned char *pointer;
void show_bytes(pointer start,size_t len){
size_t i;
for(i=0;i<len;i++){
printf("%p0x %.2x\n",start+i,start[i]); //%p表示按照16进制输出数据,数据不够则在左边补0
printf("\n");
}
}
int main(){
int a= 15213;
show_bytes((pointer)&a,sizeof(a));
system("pause");
return 0;
}
运行结果:
000000000061fe1c0x 6d
000000000061fe1d0x 3b
000000000061fe1e0x 00
000000000061fe1f0x 00
代码安全性
示例1:copy_from_kernel
在linux中有一个从内核拷贝数据到用户态的代码,源代码为:
/* Kernel memory region holding user-accessible data */
#define KSIZE 1024
char kbuf[KSIZE];
/* Copy at most maxlen bytes from kernel region to user buffer */
int copy_from_kernel(void *user_dest, int maxlen)
{
/* Byte count len is minimum of buffer size and maxlen */
int len = KSIZE < maxlen ? KSIZE : maxlen; //为什么要加长度判断?
memcpy(user_dest, kbuf, len);
return len;
}
代码段中第9行为什么要加长度判断呢?
为了阻值恶意的使用!比如:
#define MSIZE 528
void getstuff() {
char mybuf[MSIZE];
copy_from_kernel(mybuf, -MSIZE);
}
通过长度判断,可以阻止用户访问无权限的代码段。
示例2:copy_from_kernel
这个函数的目的是将ele_src中的连续内容,分成ele_cnt块,每块ele_size大小,放入新malloc的地址内
void *copy_from_kernel(void *ele_src[], int ele_cnt, size_t ele_size)
{
/*
* Allocate buffer for ele_cnt objects, each of ele_size bytes
* and copy from locations designated by ele_src
*/
void *result = malloc(ele_cnt * ele_size); //分配一块 ele_cnt*ele_size大小的空间,返回初始地址指针
if (result == NULL)
/* malloc failed */
return NULL;
void *next = result;
int i;
for (i = 0; i < ele_cnt; i++)
{
/* Copy object i to destination */
memcpy(next, ele_src[i], ele_size);
/* Move pointer to next memory region */
next += ele_size;
}
return result;
}
乍看上去没有问题,但是如果取
ele_cnt = 2^20+1
ele_size = 4096 呢?
解答:
malloc的原型是:
void *malloc(int size);
那么malloc将分配一块2^32+4096的地址,超过了int的适用范围 2^32,会分配失败!
如何保证分配正确?
对ele_cnt * ele_size的大小进行判断和限制!
整数的特性
- 整数加法的“环性”:包括交换律、结合律、加减可逆性
- 整数满足 ~x + 1 = -x 因为 ~x + x = 11111...1 = - 1
编译器对于除法的实现
C语言在进行除法运算时,对计算结果是会进行四舍五入的
对于2^k的除法,一般采用右移进行快速实现,但怎么进行小数部分的四舍五入呢?
编译器进行了优化,实际上使用的是是(x+2^k-1)/ 2^k


浙公网安备 33010602011771号