机器级程序将存储器视为一个非常大的字节数组,称为虚拟存储器。存储器的每个字节都能由唯一的数字来标识,称为地址。字长用来指明整数和指针数据的标称大小,虚拟地址空间的大小由字长决定,对于一个字长为w位的机器而言,虚拟地址的范围为0-2^w-1。计算机最小的可寻址的存储器单位——字节
一个字节的值域:00H-FFH
一、十六进制表示法
1. C表示法
以0x或0X开头的数字常量为十六进制
2. 进制转换
常用进制:二进制(B),十进制(D),八进制(O或者Q),十六进制(H)
二、字
字长决定虚拟地址空间的最大大小。
字长为w,虚拟地址的范围为1-(2^w-1)
三、数据大小
在不同字长的计算机中,相同的数据类型所占用的字节数并不相同
gcc -m32 可以在64位机上生成32位的代码
四、寻址和字节顺序
1.小端法和大端法
小端法:最低有效字节在前面——“高对高,低对低”
大端法:最高有效字节在前面
注意:
有效位的高低的判断:
以0x01234567为例,每个字节为8bit所以2个十六进制位为1个字节,即划分为01,23,45,67;在最前面的为最高有效位,最后面的为最低有效位,所以最高有效位-01,最低有效位-67。
大端法:
最高有效位在最前
小端法:
最低有效位在最前
2.强制类型转换
书28页代码
代码执行
#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_float(float x){ show_bytes((byte_pointer) &x, sizeof(float));}void show_pointer(void *x){ show_bytes((byte_pointer) &x, sizeof(void *));}void test_show_bytes(int val){ int ival = val; float fval = (float)ival; int *pval = &ival; show_int(ival); show_float(fval); show_pointer(pval);}void main(){ int val; printf("please enter an int:\n"); scanf("%d",&val); test_show_bytes(val);}
1.字符串
c语言中字符串被编码成为一个以null(值为0)字符结尾的字符数组。多使用ASCII字符码。
在使用ASCII字符码的任何系统上都能得到相同的结果,与字节顺序和字大小规则无关,所以文本数据比二进制数据具有更强的平台独立性。
2.代码
二进制代码在不同的操作系统上有不同的编码规则。
六、位运算
1.布尔代数
常用运算符号:
与: &
或: |
非: ~
异或:^
2.位向量
位向量:有固定长度为w、由0和1组成的串。
位向量的应用——表示有限集合。
3.位级运算
位运算:位向量按位进行逻辑运算,结果仍是位向量(区别于逻辑运算)
位级表达式
位级表达式的计算,要将通过转化为二进制表示进行二进制运算再转换回原来的进制来进行。
~0:生成一个全1的掩码。
七、逻辑运算
只要一个与非门,就可以完成所有的逻辑运算。
逻辑运算符
与:&&
或:||
非:!
逻辑运算的计算方法:
所有非零参数都代表TRUE,0参数代表FALSE
逻辑运算的结果:
1-TRUE,0-FALSE
逻辑运算和位运算的区别
1.只有当参数被限制为0或1时,逻辑运算才与按位运算有相同的行为。
2.如果对第一个参数求值就能确定表达式的结果,逻辑运算符就不会对后面的参数求值。
八、移位运算
1.c语言中的移位运算
(1)左移<<
x<>
右移分为逻辑右移和算术右移。
逻辑右移:
在左端补k个0,多用于无符号数移位运算
算术右移:
在左端补k个最高有效位的值,多用于有符号数移位运算。
2.Java中的移位运算
Java中用>>表示算术右移,用>>>表示逻辑右移。左移同样。
3.优先级
移位运算的优先级比算术运算(比如+、-)要低
一、整型数据类型
整型数据类型——表示有限范围的整数,每种类型都能用关键字来指定大小,还可以指定是非负数(unsigned)还是负数(默认)。这些不同大小的分配的字数会根据机器的字长和编译器有所不同。
要用C99中的“long long”类型,编译是要用 gcc -std=c99
二、无符号数的编码
0-(2^w)-1中的每一个整数和长度为w的位向量是一一对应的。
三、补码编码——有符号数的编码
关于补码,一些可能没有注意过的知识:
我们都知道补码的最高位是表示符号位,解释为负权,“权重”为-2的(w-1)次方,即无符号表示中的权重的负数。符号位为1,表示值为负,符号位为0,表示值为非负(不是正,因为有0)
补码的映射关系同样是一一对应的
补码的范围:-2^(w-1)~2^(w-1)-1,即|TMin|=|TMax|+1——因为非负数中0的存在。
无符号数编码(U)和补码(T):UMax = 2 TMax + 1
大多数情况下用补码来表示有符号数,并且具有的是典型的取值范围。
有符号数的其他表示方法:
原码.反码
四、有符号数和无符号数的转换
1.强制类型转换
强制类型转换的结果保持位值不变,只是改变了解释这些位的方式。即:这些位上的值不变,但是由于最高有效位的权重发生变化,从而导致结果发生改变。
2.c语言中的有符号数和无符号数及其转换
转换原则:底层的位保持不变。
(1)有符号数→无符号数
非负数——保持不变
负数——转换成大正数
(2)无符号数→有符号数
以2的w-1次方为界限:
小于它——保持不变
大于它——转换为负数值
c语言中同时存在有符号数和无符号数?
会隐式的将有符号数强制类型转换为无符号数,并且假设这两个数都是非负的。
五、扩展
扩展——从一个较小的数据类型转换为较大的数据类型,同时保持数值不变。
1.零扩展
多用于无符号数转换为一个更大的数据类型。
只需在开头加上0即可。
2.符号扩展
多用于补码数字转换
最高有效位是什么,就添加什么。
注:无符号和有符号数字之间的转换,和数据的大小的转换,这之间的相对顺序能够影响到一个程序的行为。具体见联系2.23。
六、截断数字
不用额外的位来扩展数值,而是减少表示一个数字的位数。而这么做可能会改变它的值,这也是溢出的一种形式。
将一个w位的数截断为k位数字时,就会丢弃高w-k位。
对于无符号数来说,就相当于 mod 2的k次幂
对于有符号数来说,先按照无符号数截断,然后再转化为有符号数
一、无符号运算
无符号运算本质上就是模运算,mod 2的w次幂。
1.加法
涉及到的相关知识有:交换群(阿贝尔群),单位元,加法逆元等等。计算起来很简单。
2.乘法
两个w位的无符号数相乘,实际上是截取了低w位,但是等价于mod 2的w次幂。
总之就是模幂运算。
二、补码运算
1.加法
两个数的w位补码之和与无符号之和有完全相同的位级表示。
2.非
(1)补码的非运算
对于范围在[-2^(w-1),2^(w-1))中的x,补码的非运算有如下两种情况:
x=-2^(w-1)时,为-2^(w-1)
x>-2^(w-1)时,为-x
(2)求位级补码非
对每一位求补,再对结果+1
设k为最右面的1的位置,将k左边的所有位取反。
3.乘法
c语言中的有符号乘法是通过将2w位的乘积截断为w位的方式实现的。也就是说,需要mod 2的w次幂。
所以:对于无符号和补码乘法来说,乘法运算的位级表示都是一样的。
三、乘以常数
在机器运算中,加法和移位(左移)相对较快
1.常数为2的k次幂的时候
直接左移k位即可
2.常数不是2的整数次幂的时候
将常数C表示为2的几个整数次幂的和,结合移位运算和加法运算。
四、除以2的幂
机器运算中,除法比乘法慢。当被除数为2的整数次幂时,通过右移来解决。右移时需要区分无符号数和补码。
需要注意:整数除法总是舍入到零
1.无符号数——逻辑右移
无符号数除以2的k次幂,就等同于对其逻辑右移k位。
2.补码——算术右移
补码进行算术左移时,需要考虑补码数的正负,因为整数除法总是舍入到零,无符号数中没有负数不必担心,但补码中有正有负,正数向下舍入到零,负数应该向上舍入到零。
五、整数运算的总结
计算机执行的“整数运算”实际上是一种模运算。
无论运算数是以无符号形式还是补码形式表示,都有完全一样或者非常类似的位级行为。
在编写c语言程序时需要谨慎食用unsigned数据类型,很可能会造成漏洞。
浮点表示对形如V=x X (2^y)的有理数进行编码,适用于:
非常大的数字
非常接近于0的数字
作为实数运算的近似值
一、二进制小数
小数的二进制表示法只能表示那些能够被写成x X (2^y)的数,其他的值只能近似的表示。
权重
以小数点为界:
左边第i位,权重为2的i次幂
右边第i位,权重为1/2的i次幂
二、IEEE浮点表示
IEEE浮点标准:
用V=(-1)^s X 2^E X M 来表示一个数:
符号:s决定这个数是正还是负。0的符号位特殊情况处理。
阶码:E对浮点数加权,权重是2的E次幂(可能为负数)
尾数:M是一个二进制小数,范围为1~2-ε或者0~1-ε(ε=1/2的n次幂)
编码规则:
单独符号位s编码符号s,占1位
k位的阶码字段exp编码阶码E
n位小数字段frac编码尾数M(同时需要依赖阶码字段的值是否为0)
两种精度
单精度(float),k=8位,n=23位,一共32位;
双精度(double),k=11位,n=52位,一共64位。
1.规格化的值
即exp的位模式既不全0也不全1的时候,这是最一般最普遍的情况,因而是规格化的。
(1)阶码字段和阶码
这里是以偏置形式表示的有符号整数。
阶码E = e-Bias
Bias=[2^(k-1)-1]
(2)小数字段和尾数
二进制小数点在小数字段最高有效位的左边。
尾数M = 1+f(隐含的以1开头的表示)
2.非规格化的值
即阶码域全为0时的数。
(1)阶码
阶码E = 1-Bias
(2)尾数
尾数M = f(小数字段的值,不包含隐含的1)
(3)非规格化的功能:
a. 提供了一种表示数值0的方法。
b. 表示那些非常接近零的数。逐渐溢出
3.特殊值
特殊值是在阶码位全为1的时候出现的。分为两种情况:
(1)无穷
小数字段全为0
(2)NaN不是一个数
小数字段非0
三、舍入
舍入运算:找到和数值x最接近的匹配值x',可以用期望的浮点形式表示出来。
IEEE浮点格式定义了四种不同的舍入方法:
1.向偶舍入(默认方法)
即:将数字向上或向下舍入,是的结果的最低有效数字为偶数。
能用于二进制小数。
2.向零舍入
即:把整数向下舍入,负数向上舍入。
3.向下舍入
正数和负数都向下舍入。
4.向上舍入
正数和负数都向上舍入。
默认的(即向偶舍入)方法可以得到最接近的匹配,其余三种可用于计算上界和下界。
四、浮点运算
1.浮点加法
浮点加法是可交换的
浮点加法不具结合性
大多数值的浮点加法都有逆元,除了无穷和NaN。
浮点加法满足单调性
2.浮点乘法
浮点乘法是可交换的
浮点乘法不具有结核性
浮点乘法的单位元为1.0
浮点乘法在加法上不具备分配性
1.假设有两个函数实现位设置bis和位清除bic操作;只利用这两个函数实现按位 | 和^操作。
int bis(int x, int y);
int bis(int x, int y);
int bool_or(int x,int y)
{
int result = bis(x,y);
return result;
}
int bool_xor(int x,int y)
{
int result = bis(bic(x,y),bic(y,x));
return result;
}
本题初始解答错位。原因:未掌握 x^y = (x&~y) | (~x&y)
2.假设x,y的字节值分别是0x66,0x39,求:
x & y = 0x20
x | y = 0x7f
~x | ~y = 0xdf
x&!y = 0x00
x && y = 0x01
x || y = 0x01
!x || !y = 0x00
7.对于32位补码形式显示的十六进制值,转化为十进制
0x1b8 = 110
0xfffffe58 = -424
3.假设在运用补码运算32位机器上对以下这些表达式求值
-2147483647-1 == 2147483648U 无符号 1 -2147483647-1<2147483647 有符号 1 -2147483647-1U <2147483647 无符号 0 -2147483647-1U <-2147483647 无符号 1
4.假设将一个4位数值(0——f)截断为一个3位数值(0——7),填写截断后的结果。
原始值:0 无符号截断值:0 补码截断值:0
原始值:2 无符号截断值:2 补码截断值:2
原始值:9 无符号截断值:1
原始值:b 无符号截断值:3
原始值:15 无符号截断值:7
原始值:-7 补码截断值:1
原始值:-1 补码截断值:-1
5.以下代码试图计算数组a[]中所有元素的和,然而当参数length=0时,会发生存储器错误。试解释原因并修改代码。
#include<stdio.h>
#define MAX 100
float sum_elements(float a[], unsigned length)
{
int i;
float result = 0;
for (i =0;i<=length-1;i++)
{
result+=a[i];
}
return result;
}
void main()
{
float a[MAX];
unsigned number;
int i;
printf("Please enter the amount of numbers in your array:\n");
scanf("%u",&number);
if(number <0)
{
printf("Wrong!\n");
return;
}
if(number == 0)
{
printf("the result is:%f\n",sum_elements(a, number));
return;
}
else
{
printf("Please enter the elements:(the tail of array should be end by 'e')\n");
for(i = 0;i<=number-1;i++)
{
scanf("%f\n",&a[i]);
}
printf("the result is:%f\n",sum_elements(a, number));
return;
}
}
6.求:
无符号:x=[100],y=[101] xy = 010100=20,截断后的xy= 100=4
有符号:x=[100],y=[101] xy = 001100=12,截断后的xy= 100=-4 (初始解答:x=[100],y=[101] xy = 10100=-12,截断后的xy= 100=-4。错误。因为:有符号的补码运算,如果运算数有负数,应该把正确结果转换成二进制)
7.对于位位置n为有效位的情况,我们要怎么样修改表达式B:(x<<n+1)-(x<<m)?
表达式变为了-(x<<m)。解析(不同于课本):n为有效位,则K为补码表示的负数,将其(认为K的二进制表示中只有n到m位是1,其余位是0)转换为二进制的绝对值形式后,发现其二进制表示中只有第m位是1,其余位都是0。那么,x*k就变成了-(x<<m)
8.浮点数对形如V = X*2^Y的数字进行编码,主要是很接近于0或者很大的数字。当一个数字不能被精确地表示为这种形式时,就必须要向上或者向下调整,即为舍入。
9.IEEE浮点标准——用V= (-1)^sM2^E来编码一个数。其中:
符号:s决定这个数是负数(s=1)还是正数(s=0),对于数值是0的符号位解释为特殊情况。 尾数:M是一个二进制小数 阶码:E对浮点数加权,可以是负数
根据以上,float:s=1位,exp=8位,frac=23位
double:s=1位,exp=11位,frac=52位
10.整数与浮点数表示同一个数字的关系?
整数与浮点数表示同一个数字时,化成二进制形式之后,可以看到,整数等于1 的最高有效位之后的数字,与浮点数小数部分的高位是相匹配的。
11.整数与浮点数转换规则?
整数->浮点数:整数转换成二进制表示,然后左移若干位得到规格化表示;取出小数部分的数值,在后面补0使其达到23位;用frac加上偏置量得到的结果用二进制表示,放在取出的部分前面,再加上一个符号位即可。