• 信息安全系统设计基础第三周学习总结


    信息安全系统设计基础第三周学习总结

    【教材第二章:信息的表示和处理】

    【学习时间:12小时】

    一、教材知识点

    1.无符号数、有符号数(2进制补码)、浮点数,从逆向角度考虑为什么会产生漏洞?

    【任何漏洞产生都必然因为系统不可更改的局限性——>无符号数、有符号数、浮点数的局限性——>无符号数或者有符号数的表示范围有限,而浮点数虽然编码范围大,但是不精确】

    2.在C语言中,所有以0X或者0x开头的数字常量都常被认为是十六进制的值

    3.计算机的字长指明的最重要的系统参数是虚拟地址空间的最大大小(字长为w,则程序最多访问2^w个字节)

    4.gcc -m32 可以在64位机上生成32位的代码

    5.字节顺序的两种表示方法:小端是“高对高、低对低”,大端与之相反

    6.代码执行

    #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("
    ");
    }
    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:
    ");
        scanf("%d",&val);
        test_show_bytes(val);
    }
    

    以12345为例,输出结果如下:

    (图1)

    说明,该笔记本(yoga2)为小端法机器。同时,还了解到,不同的机器或者操作系统使用不同的存储分配原则。

    7.文本数据比二进制数据具有更好的平台适应性。

    8.只要一个与非门,就可以完成所有的逻辑运算。

    9.要用C99中的“long long”类型,编译是要用 gcc -std=c99

    10.B2T中,最高位权重为-2^(w-1)。这也可以解释,利用补码可以把数学运算统一成加法

    11.将有符号数强转成无符号数之后,数字的数值发生改变,然而其位表示不变。(如,将-12345的补码看作无符号数字的话,则为53191.经过类型强转得到的结果也是53191)

    12.T2Uw(x)=x+2^w,x<0;U2Tw(x) = x-2^w,x>=2^(w-1)

    13.几乎所有的机器都使用补码;通常,大多数数字都默认为有符号.然而,在一个运算式中,如果有一个参数是无符号的 ,那么另外一个也会默认为无符号数

    14.怎么样让负数等于正数?由练习题2.21可以得到一些启示:在负数x后加上U,可以使其转换为(2^w+x)

    15.零扩展类似于逻辑左(右)移。即:将一个无符号数转换为一个更大的数据类型,简单地在前面加上0。符号扩展类似于算数左(右)移。即:将一个补码数字转换为一个更大的数据类型,在表示中添加最高有效位值得副本。注意:在进行数据大小、有无符号的转换时;如果要进行从short到unsigned的转换,必须经过short->int->unsigned

    16.数字截断:将一个w位的数丢弃前k位得到的数字(二进制)为:2^w mod 2^k,有可能发生溢出.

    17.算数运算的溢出,是指完整的整数结果不能放到数据类型的字长限制中去。比如,两个数的和为2^w或者更大时,就发生了溢出。

    18.补码的非:除了x=-2^(w-1)和0的非是他们本身之外,其他数字的非(加法逆元)都可以通过2^w-x获得。

    19.C语言中,有符号数字的乘法是通过将2w位截断为w位的方式来实现的。即:xy - U2T((xy)mod 2 ^w)

    20.表达式x*K,其中,K可以表示为一组从位位置n到m连续的1和其他位置的0,那么,表达式可以表示为:

    A:(x<<n)+(x<<n-1)+……+(x<<m) B:(x<<n+1)-(x<<m)

    21.除以2的幂可以用移位运算实现(右移)。事实证明,移位总是摄入到0,这一结果与整数除法的规则一样。对于非负数,算术右移和逻辑右移都可以作为除法运算;而负数则是进行算术右移。

    22.浮点数对形如V = X*2^Y的数字进行编码,主要是很接近于0或者很大的数字。当一个数字不能被精确地表示为这种形式时,就必须要向上或者向下调整,即为舍入。

    23.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位

    24.整数与浮点数表示同一个数字的关系?

    整数与浮点数表示同一个数字时,化成二进制形式之后,可以看到,整数等于1 的最高有效位之后的数字,与浮点数小数部分的高位是相匹配的。

    25.整数与浮点数转换规则?

    整数->浮点数:整数转换成二进制表示,然后小数点左移若干位得到规格化表示;取出小数部分的数值,在后面补0使其达到23位; 用frac加上偏置量得到的结果用二进制表示,放在取出的部分前面,再加上一个符号位即可。

    26.利用这一特性:x/y = (x+y-1)/y。在移位(除法)之前“偏置”这个值,通过这种方法修正不合理的输入。

    27.在IEEE浮点数表示中,以规格化表示的阶码字是以偏置形式表示的有符号数。

    28.当阶码全为1、小数域全为0时,得到的值表示无穷;当阶码全为1、小数域不全为0时,结果是NaN(not a number)

    二、课本练习题筛选

    1.

    0x503c+0x8 = 0x5044

    0x503c-0x40 = 0x4ffc

    0x503c+64 = 0x5070(原始答案错误。原因:未看到64前面没有十六进制标识)

    2.写出0x00359141、0x4a564504的二进制表示。并移动相对位置使其尽量匹配

    0x00359141 = 0000 0000 0011 0101 1001 0001 0100 0001(2进制)

    0x4a564504 = 0100 1010 0101 0110 0100 0101 0000 0100(2进制)

    0000 0000 001101011001000101000001

                *************************
    

    0100 1010 010101100100010100000100

    共有21位相匹配。整数基本上所有有效位都嵌在浮点数中。

    3.a = [01101001],b = [01010101]。计算:

    ~a = [10010110]

    ~b = [10101010]

    a&b = [01000001]

    a |b = [01111101]

    a^b = [00111100]

    4.写一段代码实现一个数组的头尾元素依次交换

    代码如下:

    #include<stdio.h>
    #define MAX 10
    void inplace_swap(int *x,int *y)
    {
        *y = *x^*y;
        *x = *x^*y;
        *y = *x^*y;
    }
    void reverse_array(int a[], int cnt)
    {
        int first,last;
        for(first = 0,last = cnt-1;first<=last;first++,last--)
            inplace_swap(&a[first], &a[last]);
    }
    void main()
    {
        int a[MAX];
        int count,i;
        printf("please enter the amount of numbers( no more than %d):
    ",MAX);
        scanf("%d",&count);
        printf("please enter numbers('e' as the end)
    ");
        for(i = 1;i<=count;i++)
        {
            scanf("%d
    ",&a[i-1]);
        }
        printf("the original array is as follow:
    ");
        for(i = 1;i<=count;i++)
        {
            printf("%d  ",a[i-1]);
        }
        reverse_array(a, count);
        printf("the new array is as follow:
    ");
        for(i = 1;i<=count;i++)
        {
            printf("%d  ",a[i-1]);
        }
    }
    

    这段代码分别以1,2,3,4和1,2,3,4,5作为输入的时候,结果如下:

    (图2)

    (图3)

    即:当数组长度为奇数的时候,输出的结果最中间的数字变为0.原因?在最后一次调用inplace_swap的时候,传入的first和last都是原数组中最中间的数字;在第一处*y = x^y时,y指向的数字就变为了0.此后,0变作为最中间的数字参与循环。解决办法?将循环条件中的first<=last 改为first<last(最中间的数字不会参与循环)即可。

    5.假设有两个函数实现位设置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)

    6.假设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

    8.

    T2TU4(-8) = 8 T2U4(-3) = 13 T2U(-2) = 14 T2U4(0) = 0

    9.假设在运用补码运算32位机器上对以下这些表达式求值

    -2147483647-1 == 2147483648U 无符号 1 -2147483647-1<2147483647 有符号 1 -2147483647-1U <2147483647 无符号 0 -2147483647-1U <-2147483647 无符号 1

    10.假设在一个采用补码运算的32位机器上执行这些函数。计算下列输入参数之后的结果:

        #include<stdio.h>
    int fun1(unsigned word)
    {
        return (int)((word<<24)>>24);
    }
    int fun2(unsigned word)
    {
        return ((int)word<<24)>>24;
    }
    void main()
    {
        int word;
        printf("please enter a number:
    ");
        scanf("%d",&word);
        printf("the result of fun1:%d
    ",fun1(word));
        printf("the result of fun2:%d
    ",fun2(word));
    }
    

    分析:fun1()是将word进行过逻辑左移和右移的结果转换为int型;而fun2()是将word先强制转换为int型,随后进行的算数左移和右移

    w:0x00000076 fun1(w)=0x00000076,fun2(w)=0x00000076

    w:0x87654321 fun1(w)=0x00000021,fun2(W)=0x00000021 (此处起初和答案有冲突。最初认为在fun2()中,w转换为的int型应该表示的是一个负数,所以逻辑移动时应该补1.后来意识到,是先进行左移即右侧六个十六进制位补f,随后右移的时候,因为最高有效位是0,所以前侧六个十六进制位补0)

    w:0x000000c9 fun1(w)=0x000000c9 fun2(w)=0xffffffc9

    11.假设将一个4位数值(0——f)截断为一个3位数值(0——7),填写截断后的结果。

    原始值:0 无符号截断值:0 补码截断值:0

    原始值:2 无符号截断值:2 补码截断值:2

    原始值:9 无符号截断值:1

    原始值:b 无符号截断值:3

    原始值:15 无符号截断值:7

    原始值:-7 补码截断值:1

    原始值:-1 补码截断值:-1

    12.以下代码试图计算数组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:
    ");
        scanf("%u",&number);
        if(number <0)
        {
            printf("Wrong!
    ");
            return;
        }
        if(number == 0)
        {
            printf("the result is:%f
    ",sum_elements(a, number));
            return;
        }
        else
        {
            printf("Please enter the elements:(the tail of array should be end by 'e')
    ");
            for(i = 0;i<=number-1;i++)
            {
                scanf("%f
    ",&a[i]);
            }
            printf("the result is:%f
    ",sum_elements(a, number));
            return;
        }
    }
    

    以正常的浮点数数组{1.2,2.6,5.7,8.6}作为输入,得到正常结果:

    (图5)

    如果以length=0输入,结果如下

    (图6)

     

    解释:原因应该在“i<=length-1”与之前声明的“unsigned length”的矛盾中。因为当输入的length是0时,length-1=0-1(无符号数运算),即模数加法,得到的是Umax。而任何数都是小于Umax的,所以比较式恒为真。则循环会访问数组a中的非法元素。简单的处理办法就是将length声明为int型。

    13.任务:写一个函数判定一个字符串是否比另一个更长。函数如下:

    size_t strlen(const char *s);
    int strlonger(char *s,char *t)
    {
        return strlen(s)-strlen(t) >0;
    }
    

    在什么情况下会产生不正确的结果?

    当s的长度小于t的长度时,strlen(s)-strlen(t) 仍然以无符号数进行运算,则会产生模数加法,使其恒大于零。

    如何修改代码?

    把return strlen(s)-strlen(t) >0改成return strlen(s)>strlen(t)即可。

    14.写一个具有如下原型的函数:

    int uadd_ok(unsigned x,unsigned y)

    当x和y的和不发生溢出时,返回1

    答: int uadd_ok(unsigned x,unsigned y)

    {
        unsigned sum = x+y;
        return sum >=x;
    }`
    

    因为机器运算能力的有限性,不可能使用x+y>2^w这样的判断语句。所以通过判断两数之和是否大于某一个被加数这样的间接方式确定。

    15.计算下列16进制数字的加法逆元。

    F 十进制:15 逆元:1 0 十进制:0 逆元:0

    16.求:

    无符号: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。错误。因为:有符号的补码运算,如果运算数有负数,应该把正确结果转换成二进制)

    17.对于位位置n为有效位的情况,我们要怎么样修改表达式B:(x<<n+1)-(x<<m)?

    表达式变为了-(x<<m)。解析(不同于课本):n为有效位,则K为补码表示的负数,将其(认为K的二进制表示中只有n到m位是1,其余位是0)转换为二进制的绝对值形式后,发现其二进制表示中只有第m位是1,其余位都是0。那么,x*k就变成了-(x<<m)

    18.写一个函数div16,对任何整数参数,返回x/16的值。不能使用四则运算和任何条件运算符、比较运算符。(假设你的机器是32位,使用补码表示,右移都是算术右移)

    (分析:正整数的除法运算很容易就通过>>4移位实现,然而还需要考虑负数的情况。如果不能使用条件语句,就要写出满足正负数的通用公式。)

    int div16(int x)
    {
        int bias = (x>>31)&0xf;//如果是负数,bias就会变成f
        return (x+bias)>>4;
    }
    

    19.在下面的代码中,我们定义了常数M和N

    #define M
    #define N
    int arith(int x,int y)
    {
        int result = 0;
        result = x*M+y/N;
        return result;
    }
    

    以下是将机器代码翻译为C语言的结果:

    int optarith(int x,int y)
    {
        int t = x;
        x<<5;
        x-=t;
        if(y<0) y+=7;
        y>>3;
        return x+y
    }
    

    问:M和N是多少? 1.x左移五位即乘32,减去1之后相当于乘31;则M=31

    2.y右移三位相当于除以8;则N=8

    20.假设我们对有符号数字使用补码运算。变量的初始化和声明如下:

    int x = foo();
    int y = bar();
    unsigned ux = x;
    unsigned uy = y;
    

    对下面每个表达式,证明其真假。

    1)x*x >=0 假。可以x = 65535为例;此时,按照有符号数乘法,得到的数转换为二进制之后为0xFFFE001,为负数。

    2)x~y+uxuy ==-x 真。~y等于-y-1,uxuy == xy。因此,等式左右两边等价。

    三、问题总结

    1.P63:

    通过类似的推理,我们可以得出,对于一个位模式为[x(w-1),x(w-2),……,0,……,0]的补码数x,以及在0<=k<=w范围内的任一k,位模式为[x(w-k-1),x(w-k-2),……,0,……,0]就是x*2^k的补码表示

    为什么截断前面的k位、后面补上0之后,就是一个乘式结果的补码表示?

    P66练习题2.42

    写一个函数div16,对任何整数参数,返回x/16的值。不能使用四则运算和任何条件运算符、比较运算符。(假设你的机器是32位,使用补码表示,右移都是算术右移)

    int div16(int x)
    {
        int bias = (x>>31)&0xf;//如果是负数,bias就会变成f
        return (x+bias)>>4;
    }
    

    不太理解如何证明负数运算时,加上bias(即f)之后就可以直接右移四位?

    P67练习题2.44

    E.x>0||-x>0

    假。设x=-2147483648(Tmin32),则x和-x都为负数

    如何判断x的相反数是多少?

    四、学习心得

    通过本次“边读书,边思考,边记录”的过程,我深刻地意识到“精读”背后要多付出的精力、时间,更体会到与泛读甚至浏览完全不在同一个层次上的收获。第二章一共60页,坚持每一页的每一句话(不能保证是每一个字)都看到心里去,说不乏味不疲倦是不可能的。在十一假期里,我用了四天的时间,每天都看上几十页,积少成多地去读书。然而,时间上的持久带来了短期记忆模糊的问题。这时候,靠自己的笔记就可以有效地弥补记忆上的模糊,让知识点和代码“夯实”在脑子里。

    这样的收获是显著的;我开始跟随老师所提出的要点去走,跟着书中的思路去走,书中所提到的引用与老师所提到的要点就好像路标,我看到了所有的路标之后,路就变得格外好走。

  • 相关阅读:
    【python3.8】斐波拉契数列实现
    【Java开发基础】计算两个毫秒之间相差多少天
    FileZilla关闭更新检测
    【Java开发基础】生成两个正数之间的随机数
    Thinkphp6笔记十九:加载自定义配置
    linux系统安装坚果云
    vim NERDTree 目录插件常见用法
    vim 插件管理
    vim 窗口分割 以及 tab常用操作
    vim Ntree 树形目录常见用法
  • 原文地址:https://www.cnblogs.com/lwr-/p/4854939.html
Copyright © 2020-2023  润新知