标签:十六进制 CSAPP 字节 show int 北邮 二进制 32 第二章
-
为什么使用二进制?
二值信号能更容易地被表示、存储、传输 -
三种最重要的数字表示:
- 无符号
- 补码:表示有符号整数
- 浮点数
-
计算的表示是有限数量的,因此,答案太大时,结果会溢出
-
浮点运算的精度有限,不可结合(指-1 + 1 != 0)
-
整数能表示小范围的精确的数值,浮点数能表示大范围的近似的数值
-
大量计算机的安全漏洞都是由于计算机算数运算的微妙细节引起的
信息存储
-
最小的可寻址单位:byte,8个位组成。
-
虚拟内存:将内存视作一个非常大的char数组
-
尽管C编译器维护着指针的类型信息,但是实际生成的机器级程序不包含这个信息
-
每个程序对象可以视为一个字节块,而程序本身就是一个字节序列
十六进制表示法
-
为什么使用十六进制表示法:
一个字节8位,有16种取值,使用二进制过于冗长,使用十进制需要进制转换。因此,十六进制刚刚好。 -
二进制、十六进制、十进制之间的相互转换
二进制<->十六进制,可以一次执行一个十六进制数组的转换,4位二进制一组。不足4的倍数,将最左边的一组用0补足。
十六进制<->十进制:窍门:记住A、C、F对应的数字,将B、D、E的值通过计算与前三个值的关系记住(A:10 C:12 F:15)
当x = 2^n时,只要记住x写成二进制就是1后面跟n个0,就很容易写成十六进制。将n表示为i + 4j的形式。(0 <= i <= 3)(0: 0, 1: 2, 2: 4, 3: 8)
例子:2048 = 2^11, 11 = 4 × 2 + 3,=> 0x800
十进制->十六进制:用16除,得商q与余数r=> x = q · 16 + r。倒着来:例如余数12 2 11 12 4,则从4开始,转为16进制:4 C B 2 C
字数据大小
-
字长决定的最重要的系统参数:虚拟地址空间的最大大小
-
字长为w位,虚拟地址的范围:0~2^w - 1
-
大多数64位机器能运行32位机器编译的程序(向后兼容)
-
32位程序/64位程序:程序是如何编译的,而不是运行的机器类型
-
计算机和编译器支持多种不同方式编码的数字格式
-
为了避免由于编译器不同导致的错误,C99引入了一类数据类型,例如int_32t与int_64t,大小固定,不随编译器变化而变化(stdint.h)
-
char:不保证它是有符号或者无符号
-
可移植性的一个方面:使得程序对不同数据类型的确切大小不敏感
-
注意:在32位程序中,char的指针为4位,而是64位程序中,char的指针位8位
寻址与字节顺序
- 对跨多字节对象建立两个规则(如int):
- 对象地址在何处
- 如何排序
如int a,地址为&a = 0x100,字节排列为0x100、0x101、0x102、0x103
- 假设一个变量值:0x01234567 高位<-低位
->地址变大方向
大端法排序:01 23 45 67:高位->低位
小端法排序:67 45 23 01:低位->高位
大多数Intel兼容机使用小端模式
双端法:可以进行配置
实际情况:使用了特定操作系统后,字节顺序便固定了下来
Android与iOS只能使用小端模式
- 对于大多数应用程序的程序员来说,机器使用的字节顺序是完全不可见的
产生影响的情况:
- 网络传输时需要遵守有关字节顺序的规则
- 小端法机器生成的机器代码低位在左边,高位在右边,要反着读
- 编写规避正常类型系统的程序。C语言可以使用强制类型转换或联合类型来允许一种数据类型引用一个与创建这个对象时定义的数据类型与该数据类型不同的对象。
#include <stdio.h>
typedef unsigned char *byte_pointer;
void show_bytes(byte_pointer start, size_t len) // 打印出每个十六进制表示的字节
{
size_t 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_float(float x)
{
show_bytes((byte_pointer) &x, sizeof(float));
}
void show_pointer(void *x)
{
show_bytes((byte_pointer) &x, sizeof(void *));
}
int main()
{
int a = 2;
show_int(a);
show_float(a);
show_pointer(&a);
return 0;
}
- 解释:
将指针强制类型转换为unsighed char*,系统就会将其视作一个字节序列
该指针将被看作最低字节地址
使用sizeof而不是一个固定的值表示其对象大小,增加其可移植性
指针值不同的原因:不同机器、操作系统使用不同的存储分配规则
Linux 32、window使用4字节的指针,linux 64使用8字节地址
整型数与浮点数使用截然不同的编码方法
表示字符串
- 十进制数字x的ASCII码为:0x3x
- 使用ASCII码作为字符码的任何系统都是这样的,与字节顺序和字大小顺序无关。
- 文本数据比二进制数据更具平台独立性
“12345” -> 0x31 32 33 34 35 00
表示代码
- 二进制代码是不兼容的,二进制代码很少能在不同机器和操作系统组合之间移植
- 不同的机器类型使用不同的且不兼容的指令和编码方式
- 从机器的角度看,程序仅仅只是字节序列
布尔代数简介
-
将与、或、非、异或的布尔运算扩展到位向量的运算
-
位向量:固定长度为w、由0和1组成的串
-
布尔环:a^a = 0 ^ 0 = 1 ^ 1 = 0 => (a ^ b) ^ a = b
-
位向量 的一个应用:表示有限集合
使用位向量 [ a w − 1 , . . . , a 1 , a 0 ] [a_{w-1}, ..., a_1, a_0] [aw−1,...,a1,a0] 编码任何子集 0 , 1 , . . . , w − 1 {0, 1, ..., w - 1} 0,1,...,w−1 -
在大量实际应用中,我们能看到使用位向量来对集合编码
(不是很懂)
C语言中的位级运算
-
确定一个位级表达式结果的最好方式,就是将十六进制的参数扩展成二进制表示并执行二进制计算,然后再转换回十六进制
-
一个常见的错误:对布尔环没有了解而导致的错误
-
位级运算的一个常见用法是实现掩码运算
-
掩码:位模式,表示从一个字中选出的位的集合
-
例子:x & 0xFF = 0x000000XX,其它字节将被覆盖
-
~0将生成一个全1的掩码,相比于0xFFFFFFFF,这样的代码更具备可移植性
x ^ y = (x & ~y) | (~x & y)
C语言中的逻辑运算
- 逻辑运算:只有0与1两个值
- 只有在参数仅为0与1时才与位运算有一样的行为模式
- 重要区别二:如果第一个参数求值就能确定结果,那么就不会对第二个参数求值
a && 1 / a不会导致除以0
p && *p++
不会导致引用空指针
x == y <=> !(x ^ y)
C语言中的移位运算
-
左移位:向左移动k位,丢弃最高的k位,用0补足。从左至右可结合
-
右移位:分为逻辑右移和算术右移。逻辑右移:左端补k个0。算术位移:左端补k个最高有效位的值(在有符号整数的运算中很有用)
-
C语言标准没有明确定义有符号数使用哪种类型的右移,导致任何一种只假设其中一种的情况的代码都有可移植性的问题
-
但是,事实上几乎所有的实现都是采用算术右移的方式
-
而对于无符号数,右移必须是逻辑右移
-
Java中,x>>k表示算术位移,x>>>k表示逻辑位移
-
在很多机器上,当移动一个w位的值时,移位指令只考虑位移量的低log2 w位,因此实际上的位移量是通过计算k mod w得到的
int lval = 0xFEDCBA98 << 32 => 0xFEDCBA98 32 % 32 = 0
int aval = 0xFEDCBA98 >> 36 => 0xFFEDCBA9 36 % 32 = 4
-
但是在C语言上,这一点是没有保证的
-
因此应当尽量避免出现k >= w的情况
-
加减法的优先级高于位移运算,因此当优先级不确定时,最好加上括号
上课笔记
对齐
请移步博客:内存对齐
或CSDN博客内存对齐
标签:十六进制,CSAPP,字节,show,int,北邮,二进制,32,第二章 来源: https://blog.csdn.net/weixin_45206746/article/details/111340745
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。