《信息安全系统设计基础》第3周学习总结
一、三种重要的数字表示
1.无符号数、有符号数、浮点数
正数的原码、反码以及补码是其本身。
负数的原码是其本身,反码是对原码除符号位之外的各位取反,补码则是反码加1。
2.为什么用补码表示
能够统一+0和-0的表示
采用原码表示,+0的二进制表示形式为0 000 0000,而-0的二进制表示形式为1 000 0000;
采用反码表示,+0的二进制表示形式为0 000 0000,而-0的二进制表示形式为1 111 1111;
采用补码表示,+0的二进制表示形式为0 000 0000,而-0的二进制表示形式为1 111 1111+1=1 0000 0000,因为计算机会进行截断,只取低8位,所以-0的补码表示形式为0000 0000。
补码的表示范围比原码和反码表示的范围都要大。用补码能够表示的范围为-128~127,0~127分别用00000000~01111111来表示,而-127~-1则用10000001~11111111来表示,多出的10000000则用来表示-128。
对于有符号整数的运算能够把符号位同数值位为一起处理
如果把符号位单独考虑的话,CPU指令还要特意对最高位进行判断,使计算机的最底层实现变得复杂。
3.整数溢出漏洞
参考资料:整数溢出与程序安全
一个整数是一个固定的长度,它能存储的最大值是固定的,当尝试去存储一个大于这个固定的最大值时,将会导致一个整数溢出。
整数溢出将会导致"不能确定的行为"。大多数编译器会忽略整数溢出,致使整数溢出没有立即察觉,因此没有办法去用一个应用程序来判断先前计算的结果在实际上是否也是正确的,可导致某些类型的bugs,缓冲区溢出等。
二、信息存储
1.字
每个计算机都有一个字长,指明整数和指针数据的标称大小。因为虚拟地址是以这样的一个字来编码的,所以字长最重要的系统参数就是虚拟地址空间的最大大小。
对于一个字长为w位的机器而言,虚拟地址的范围为0~2^w-1,程序最多访问2^w字节。
2.gcc -m32/64
当没有-m32或-m64参数时,一般情况下会生成跟操作系统位数一致的代码
gcc -m32 可以在64位机上(比如实验楼的环境)生成32位的代码
3.字节顺序
字节顺序是网络编程的基础,是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有小端、大端两种字节顺序。
小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处。
大端字节序是高字节数据存放在低地址处,低字节数据存放在高地址处。
4.布尔代数
逻辑运算
结果是1或0
所有逻辑运算都可以用与、或、非表达(最大式、最小式)而与或非可以用“与非”或“或非”表达,所以,只要一个与非门,就可以完成所有的逻辑运算。
逻辑与(&&) 遇0为0;
逻辑或(||) 遇1为1;
逻辑非 遇0为1,遇1为0;
位运算
结果是位向量
按位与(&) 二进制每一位遇0为0;
按位或(|) 二进制每一位遇1为1;
按位异或(^) 0^0=0,0^1=1,1^0=1,1^1=0;
按位取反(~) 二进制每一位取反。
掩码运算
掩码是位运算的重要应用,这里掩码是一个特定位模式,表示从一个字中选择一个位的集合。对特定位可以置一,可以清零。
逻辑运算与掩码运算
逻辑运算结果是1或0,位运算结果是位向量
如果对第一个参数求值就能确定表达式的结果,逻辑运算符就不会对第二个参数求值。
三、整数表示
1.整型数据类型
- char 字符型数据,占用一个字节
- unsigned char 无符号字符型数据,占用一个字节
- short 短整形数据,占用两个字节
- unsigned short 无符号短整型数据,占用两个字节
- int 整形数据,占用两个字节
- unsigned int 无符号整型数据,占用两个字节
- long 长整型数据,占用四个字节
- unsigned long 无符号长整型数据,占用四个字节
数据类型long long是在ISO C99中引入的。(编译:gcc -std=c99)。
2.无符号数的编码
对于长度为w的位向量,都有一个唯一的值与之对应;反过来,在0~2^w-1之间的每一个整数都有一个唯一的长度为w的位向量二进制表示与之对应。
3.补码编码
有符号整数最常见的表示方法:二进制补码形式。
二进制补码形式的三个特点:
二进制补码的范围是不对称的:|TMin|=|TMax|+1,即不存在与最小值相对应的整数,这容易造成程序中细微的错误。
位数相同的前提下,无符号数的最大值刚好是二进制补码最大值的2倍加1:UMax=2TMax+1。
二进制补码中的-1与UMax有相同的位表示——全1位串。
ANSI C标准并未规定使用二进制补码形式来表示有符号整数,但是几乎所有的机器都是这么做的。补码利用寄存器长度固定的特性简化数学运算,将数学运算统一成加法,只要一个加法器就可以实现所有的数学运算。
4.有符号整数的另外两种标准表示方法
二进制反码形式:与二进制补码的表示方法类似,区别在于最高有效位的权值不同。
原码:最高有效位是符号位,确定剩下的位取负权值还是正权值。
这两种表示都有一个奇怪的属性,即对于数字0存在两种编码。对于两种方法,[00..0]都被解释成+0 ,而-0在二进制反码中表示为[11..1] ,在原码中表示为[10..0]。
虽然曾经有过基于二进制反码表示法的机器,但几乎所有现代机器都使用二进制补码。
符号数值编码方式使用在浮点数的表示中。
5.符号数与无符号数之间的转换
ANSI C规定在无符号整数和有符号整数之间进行强制类型转换时,并未改变对象的位模式,改变的是位模式的解释方式。处理同样字长的有符号数和无符号数之间相互转换的一般规则是:数值可能会改变,位模式不变。
T2U:补码到无符号数的转换
U2T:无符号数到补码的转换
有符号数转换为无符号数时,负数转换为大的正数(可以理解为原值加上2的n次方),而正数保持不变。
x<0 T2Uw(x)=x+2^w
x>0 T2Uw(x)=x+2^w
(*w表示数据类型的位数)
无符号数转换为有符号数时,对于小的数将保持原值,对于大的数则转换为负数(可以理解为原值减去2的n次方)
u<2^(w-1) U2Tw(u)=u
u>=2^(w-1) U2Tw(u)=u-2^w
(*w表示数据类型的位数)
6.C中有符号和无符号数
通常认为生命的常量是有符号的,要创建一个无符号常量,必须加上后缀字符'U'或者'u'。
C语言允许无符号数和有符号数之间的转换,转换的原则是底层的位保持不变。
显式转换:强制类型转换。
隐式转换:一种类型的表达式被赋值给另外一种类型的变量时。
C语言在处理同时包含无符号数和有符号数的表达式时,会隐式的将有符号数转换为无符号数,并假设这两个操作数都是非负的,然后执行运算。这一特性对于标准的算术运算来说无多大差异,但对于像"<"和">"这样的关系运算符,有时会导致和直觉不相符的结果。
例:当一个有符号数“-1”与无符号数“0”用关系运算符连接时,会自动的将-1隐式转换为无符号数4294967295(假设是一个使用补码的32位机器),负数变成了正数,“-1<0u”的结果值就是0。
一、三种重要的数字表示
1.无符号数、有符号数、浮点数
正数的原码、反码以及补码是其本身。
负数的原码是其本身,反码是对原码除符号位之外的各位取反,补码则是反码加1。
2.为什么用补码表示
能够统一+0和-0的表示
采用原码表示,+0的二进制表示形式为0 000 0000,而-0的二进制表示形式为1 000 0000;
采用反码表示,+0的二进制表示形式为0 000 0000,而-0的二进制表示形式为1 111 1111;
采用补码表示,+0的二进制表示形式为0 000 0000,而-0的二进制表示形式为1 111 1111+1=1 0000 0000,因为计算机会进行截断,只取低8位,所以-0的补码表示形式为0000 0000。
补码的表示范围比原码和反码表示的范围都要大。用补码能够表示的范围为-128~127,0~127分别用00000000~01111111来表示,而-127~-1则用10000001~11111111来表示,多出的10000000则用来表示-128。
对于有符号整数的运算能够把符号位同数值位为一起处理
如果把符号位单独考虑的话,CPU指令还要特意对最高位进行判断,使计算机的最底层实现变得复杂。
3.整数溢出漏洞
参考资料:整数溢出与程序安全
一个整数是一个固定的长度,它能存储的最大值是固定的,当尝试去存储一个大于这个固定的最大值时,将会导致一个整数溢出。
整数溢出将会导致"不能确定的行为"。大多数编译器会忽略整数溢出,致使整数溢出没有立即察觉,因此没有办法去用一个应用程序来判断先前计算的结果在实际上是否也是正确的,可导致某些类型的bugs,缓冲区溢出等。
二、信息存储
1.字
每个计算机都有一个字长,指明整数和指针数据的标称大小。因为虚拟地址是以这样的一个字来编码的,所以字长最重要的系统参数就是虚拟地址空间的最大大小。
对于一个字长为w位的机器而言,虚拟地址的范围为0~2^w-1,程序最多访问2^w字节。
2.gcc -m32/64
当没有-m32或-m64参数时,一般情况下会生成跟操作系统位数一致的代码
gcc -m32 可以在64位机上(比如实验楼的环境)生成32位的代码
3.字节顺序
字节顺序是网络编程的基础,是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有小端、大端两种字节顺序。
小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处。
大端字节序是高字节数据存放在低地址处,低字节数据存放在高地址处。
4.布尔代数
逻辑运算
结果是1或0
所有逻辑运算都可以用与、或、非表达(最大式、最小式)而与或非可以用“与非”或“或非”表达,所以,只要一个与非门,就可以完成所有的逻辑运算。
逻辑与(&&) 遇0为0;
逻辑或(||) 遇1为1;
逻辑非 遇0为1,遇1为0;
位运算
结果是位向量
按位与(&) 二进制每一位遇0为0;
按位或(|) 二进制每一位遇1为1;
按位异或(^) 0^0=0,0^1=1,1^0=1,1^1=0;
按位取反(~) 二进制每一位取反。
掩码运算
掩码是位运算的重要应用,这里掩码是一个特定位模式,表示从一个字中选择一个位的集合。对特定位可以置一,可以清零。
逻辑运算与掩码运算
逻辑运算结果是1或0,位运算结果是位向量
如果对第一个参数求值就能确定表达式的结果,逻辑运算符就不会对第二个参数求值。
三、整数表示
1.整型数据类型
- char 字符型数据,占用一个字节
- unsigned char 无符号字符型数据,占用一个字节
- short 短整形数据,占用两个字节
- unsigned short 无符号短整型数据,占用两个字节
- int 整形数据,占用两个字节
- unsigned int 无符号整型数据,占用两个字节
- long 长整型数据,占用四个字节
- unsigned long 无符号长整型数据,占用四个字节
数据类型long long是在ISO C99中引入的。(编译:gcc -std=c99)。
2.无符号数的编码
对于长度为w的位向量,都有一个唯一的值与之对应;反过来,在0~2^w-1之间的每一个整数都有一个唯一的长度为w的位向量二进制表示与之对应。
3.补码编码
有符号整数最常见的表示方法:二进制补码形式。
二进制补码形式的三个特点:
二进制补码的范围是不对称的:|TMin|=|TMax|+1,即不存在与最小值相对应的整数,这容易造成程序中细微的错误。
位数相同的前提下,无符号数的最大值刚好是二进制补码最大值的2倍加1:UMax=2TMax+1。
二进制补码中的-1与UMax有相同的位表示——全1位串。
ANSI C标准并未规定使用二进制补码形式来表示有符号整数,但是几乎所有的机器都是这么做的。补码利用寄存器长度固定的特性简化数学运算,将数学运算统一成加法,只要一个加法器就可以实现所有的数学运算。
4.有符号整数的另外两种标准表示方法
二进制反码形式:与二进制补码的表示方法类似,区别在于最高有效位的权值不同。
原码:最高有效位是符号位,确定剩下的位取负权值还是正权值。
这两种表示都有一个奇怪的属性,即对于数字0存在两种编码。对于两种方法,[00..0]都被解释成+0 ,而-0在二进制反码中表示为[11..1] ,在原码中表示为[10..0]。
虽然曾经有过基于二进制反码表示法的机器,但几乎所有现代机器都使用二进制补码。
符号数值编码方式使用在浮点数的表示中。
5.符号数与无符号数之间的转换
ANSI C规定在无符号整数和有符号整数之间进行强制类型转换时,并未改变对象的位模式,改变的是位模式的解释方式。处理同样字长的有符号数和无符号数之间相互转换的一般规则是:数值可能会改变,位模式不变。
T2U:补码到无符号数的转换
U2T:无符号数到补码的转换
有符号数转换为无符号数时,负数转换为大的正数(可以理解为原值加上2的n次方),而正数保持不变。
x<0 T2Uw(x)=x+2^w
x>0 T2Uw(x)=x+2^w
(*w表示数据类型的位数)
无符号数转换为有符号数时,对于小的数将保持原值,对于大的数则转换为负数(可以理解为原值减去2的n次方)
u<2^(w-1) U2Tw(u)=u
u>=2^(w-1) U2Tw(u)=u-2^w
(*w表示数据类型的位数)
6.C中有符号和无符号数
通常认为生命的常量是有符号的,要创建一个无符号常量,必须加上后缀字符'U'或者'u'。
C语言允许无符号数和有符号数之间的转换,转换的原则是底层的位保持不变。
显式转换:强制类型转换。
隐式转换:一种类型的表达式被赋值给另外一种类型的变量时。
C语言在处理同时包含无符号数和有符号数的表达式时,会隐式的将有符号数转换为无符号数,并假设这两个操作数都是非负的,然后执行运算。这一特性对于标准的算术运算来说无多大差异,但对于像"<"和">"这样的关系运算符,有时会导致和直觉不相符的结果。
例:当一个有符号数“-1”与无符号数“0”用关系运算符连接时,会自动的将-1隐式转换为无符号数4294967295(假设是一个使用补码的32位机器),负数变成了正数,“-1<0u”的结果值就是0。
四、 浮点数
1.概述
浮点表示对形如 V=x*2^y 的有理数进行编码
适用于:非常大的数字(|V|>>0)、非常接近于0的数字(|V|<<1)、实数运算的近似值。
IEEE浮点标准:IEEE标准754
2.二进制小数
二进制点左边第i位,权为2^i;右边第i位,权为(1/2)^i。
增加二进制表示的长度可以提高表示的精度。
3.IEEE浮点格式
IEEE浮点标准
表示一个数: V=(-1)^s * M * 2^E
符号:s决定这个数是正还是负。0的符号位特殊情况处理。
尾数:M是一个二进制小数,范围为1~2-ε或者0~1-ε,ε=(1/2)^n.
阶码:E对浮点数加权,权重是2的E次幂(可能为负数)。
浮点数的位表示划分为三个字段,分别对这些值进行编码
一个单独的符号位 s 直接编码符号 s.
k位的阶码字段 exp = e(k-1)……e1e0编码阶码 E.
n位小数字段 frac = f(n-1)……f1f0编码尾数 M,但是编码出来的值也依赖于阶码字段的值是否等于0.
浮点数可分为三种表达方式
规格化值
非规格化值
特殊值
1)规格化的值
exp的位模式既不全0也不全1的时候,这是最一般最普遍的情况,因而是规格化的。
阶码字段被解释为以偏置形式表示的有符号整数。
阶码: E = e-Bias
e:无符号整数
Bias:偏置值,Bias=[2^(k-1)-1]
小数字段frac的解释为描述小数值f,二进制小数点在小数字段最高有效位的左边。
尾数 M = 1+f(有时也可称作隐含的以1开头的表示)
2)非规格化的值
阶码域全为0时的数。
阶码: E = 1-Bias
尾数: M = f(小数字段的值,不包含隐含的1)
功能:
提供了一种表示数值0的方法。
表示那些非常接近0.0的数,提供一种“逐渐溢出”的属性。
3)特殊值
特殊值是在阶码位全为1的时候出现的。
小数域全为0时,数值用于表示无穷:符号位为0表示正无穷,符号位为1表示负无穷。
小数域非全0时,数值用于表示"NaN"(Not a Number)。这样的数值用于表示诸如对(-1)^0.5这样无意义的结果。
4.舍入
因为表示方法限制了浮点数的范围和精度,浮点运算只能近似的表示实数运算。
舍入运算的任务:找到和数值x最接近的匹配值x',可以用期望的浮点形式表示出来。
IEEE浮点格式定义了四种不同的舍入方法:
向偶舍入(默认) 将数字向上或向下舍入,是的结果的最低有效数字为偶数。能用于二进制小数。
向零舍入 把整数向下舍入,负数向上舍入。
向下舍入 正数和负数都向下舍入。
向上舍入 正数和负数都向上舍入。
默认的(即向偶舍入)方法可以得到最接近的匹配,其余三种产生实际值的确界。
5.浮点运算
浮点加法
可交换
不具结合性:缺少的重要群属性
大多数值的浮点加法都有逆元,除了无穷和NaN。
满足单调性
浮点乘法
可交换的
不具有结合性:可能发生溢出,或由于舍入而失去精度。
乘法单位元为1.0
在加法上不具备分配性
在一定条件下满足单调性(无符号或补码的乘法没有这些单调性属性)
6.C语言中的浮点数
C提供两种浮点类型:float和double,在支持IEEE浮点标准的机器上分别表示单精度和双精度浮点数。
C标准并未明确要求机器在浮点数的表示上采取IEEE标准 。
int、float、double之间的强制类型转换
int → float 数字不会溢出,但有可能舍入
int/float → double 保留精确的数值
double → float 由于精确度变小,可能溢出为±∞,也有可能被舍入
float/double → int 可能向零舍入,可能溢出。
1.概述
浮点表示对形如 V=x*2^y 的有理数进行编码
适用于:非常大的数字(|V|>>0)、非常接近于0的数字(|V|<<1)、实数运算的近似值。
IEEE浮点标准:IEEE标准754
2.二进制小数
二进制点左边第i位,权为2^i;右边第i位,权为(1/2)^i。
增加二进制表示的长度可以提高表示的精度。
3.IEEE浮点格式
IEEE浮点标准
表示一个数: V=(-1)^s * M * 2^E
符号:s决定这个数是正还是负。0的符号位特殊情况处理。
尾数:M是一个二进制小数,范围为1~2-ε或者0~1-ε,ε=(1/2)^n.
阶码:E对浮点数加权,权重是2的E次幂(可能为负数)。
浮点数的位表示划分为三个字段,分别对这些值进行编码
一个单独的符号位 s 直接编码符号 s.
k位的阶码字段 exp = e(k-1)……e1e0编码阶码 E.
n位小数字段 frac = f(n-1)……f1f0编码尾数 M,但是编码出来的值也依赖于阶码字段的值是否等于0.
浮点数可分为三种表达方式
规格化值
非规格化值
特殊值
1)规格化的值
exp的位模式既不全0也不全1的时候,这是最一般最普遍的情况,因而是规格化的。
阶码字段被解释为以偏置形式表示的有符号整数。
阶码: E = e-Bias
e:无符号整数
Bias:偏置值,Bias=[2^(k-1)-1]
小数字段frac的解释为描述小数值f,二进制小数点在小数字段最高有效位的左边。
尾数 M = 1+f(有时也可称作隐含的以1开头的表示)
2)非规格化的值
阶码域全为0时的数。
阶码: E = 1-Bias
尾数: M = f(小数字段的值,不包含隐含的1)
功能:
提供了一种表示数值0的方法。
表示那些非常接近0.0的数,提供一种“逐渐溢出”的属性。
3)特殊值
特殊值是在阶码位全为1的时候出现的。
小数域全为0时,数值用于表示无穷:符号位为0表示正无穷,符号位为1表示负无穷。
小数域非全0时,数值用于表示"NaN"(Not a Number)。这样的数值用于表示诸如对(-1)^0.5这样无意义的结果。
4.舍入
因为表示方法限制了浮点数的范围和精度,浮点运算只能近似的表示实数运算。
舍入运算的任务:找到和数值x最接近的匹配值x',可以用期望的浮点形式表示出来。
IEEE浮点格式定义了四种不同的舍入方法:
向偶舍入(默认) 将数字向上或向下舍入,是的结果的最低有效数字为偶数。能用于二进制小数。
向零舍入 把整数向下舍入,负数向上舍入。
向下舍入 正数和负数都向下舍入。
向上舍入 正数和负数都向上舍入。
默认的(即向偶舍入)方法可以得到最接近的匹配,其余三种产生实际值的确界。
5.浮点运算
浮点加法
可交换
不具结合性:缺少的重要群属性
大多数值的浮点加法都有逆元,除了无穷和NaN。
满足单调性
浮点乘法
可交换的
不具有结合性:可能发生溢出,或由于舍入而失去精度。
乘法单位元为1.0
在加法上不具备分配性
在一定条件下满足单调性(无符号或补码的乘法没有这些单调性属性)
6.C语言中的浮点数
C提供两种浮点类型:float和double,在支持IEEE浮点标准的机器上分别表示单精度和双精度浮点数。
C标准并未明确要求机器在浮点数的表示上采取IEEE标准 。
int、float、double之间的强制类型转换
int → float 数字不会溢出,但有可能舍入
int/float → double 保留精确的数值
double → float 由于精确度变小,可能溢出为±∞,也有可能被舍入
float/double → int 可能向零舍入,可能溢出。
以下是所总结的程序机器表示结构图