签到

05月05日
尚未签到

共有回帖数 0

    幻梦如戏

    等级:
    这篇帖子源自本人发表在【汇编语言吧】“计算机学习之旅” 的后半部分。
    原帖链接:http://tieba.baidu.com/p/3288451756?pn=1
    ----------------------------------------------------------------------------------------
    我个人把计算机的学习分成如下阶段,但事实上,真正计算机入门至少也得学完计算机本科阶段的所有课程。目前本人正处在【进阶】阶段:

    一、【入门】
    ------认真学完C Primer Plus就算入门了;


    二、【初级】
    ------认真学完C++ Primer Plus、汇编语言、Windows程序设计(C/SDK)就算初级了;


    三、【进阶】
    ------认真学完数据结构、组成原理、操作系统;


    四、【高阶】
    ------算法导论、编译原理、计算机体系结构、Linux源码分析。
    题外话:------如何阅读《深入理解计算机系统》


    一、首先你得把封面封底认真看一下,至少也要瞄一眼作者是谁吧。


    二、这套丛书是“计算机科学丛书”,出版者的话你可以不看,我认真看了,这里给你提供两个重要的信息:
    ------1、这套丛书是从Andrew S. Tanenbaum,Bjarne Stroustrup,Brain W.Kernighan,Dennis Ritchie,Donald E.Knuth等大师甄选出来的经典名作。


    Andrew S. Tanenbaum非常有名的计算机教育家,他的书都很有名,比如《现代操作系统》、《操作系统设计与实现》、《计算机网络》等等,本人不久前买了本操作系统设计与实现,目前看完了第二章,感觉比较浅显易懂,不像其他的操作系统原理书籍深奥。另外作者开发了与UNIX兼容的操作系统MINIX,Linux就是由Minix发展过来的,据说Linus Torvalds(Linux之父)是Tanenbaum的学生。


    Bjarne Stroustrup是C++之父,在数学和计算机科学中都获得了大师级的认证。
    Brain W.Kernighan是Unix之父。
    Dennis Ritchie是C语言之父,哈佛大学 学物理和数学出身的。
    Donald E.Knuth,《计算机程序设计艺术》的作者,就不用多说了,比较变态!


    ------历史上很多计算机科学家都是数学牛人,比如冯若依曼,图灵,香农等等。

    所以你要找书的话,直接输入“计算机科学丛书”就会发现很多经典的大师著作。


    2、这套丛书有姊妹篇,“经典原版书库”,喜欢看英文原版的同学有福了。


    三、译者序这部分你完全可以不看,但里面有一句话很重要,我摘录下来“本书是一座桥梁,它帮助程序员衔接了计算机系统各个领域的知识,为程序员构造一个概念性框架。要想获取更多关于计算机系统结构、操作系统、编译器、网络、并发编程方面的知识,还需要进一步阅读相关书籍”,最后一句是才是重点,学海无涯。。。。。。


    四、前言部分
    这必须得认真的读,不能跳过。
    作者自云:领会这本书,你将成为极少数的“牛人”。------很有吸引力的字眼


    读者所应该具备的背景知识:作者认为只要有一个Linux系统再加上C,C++就能阅读了。本人不完全赞同,至少还需要汇编语言的基础知识,再加上一点组成原理的知识(如果不喜欢深奥的理论,那么《编码的奥秘》是一个很好的选择)。


    后面就是如何阅读本书了,作者给出了很好的建议,总结起来就是两点:多做题多实践(网站会提供本书的实验)。最后,这么经典的作品,至少也得来回看三遍吧。


    ----------------------------------本楼内容完全可以不看--------------------------------------
    第一章 计算机系统漫游
    -----------------------------
    本章是对整本书内容的一个高度抽象。注意,我们这里用了抽象两字,而不是概括!!!


    抽象------应该是计算机里面最为重要的概念了,没有之一!


    第一次接触到抽象,是在C Primer Plus的函数一章,作者做了一个很形象的比喻----黑盒子。
    是的,黑盒子---隐藏了具体的细节,程序员只要知道函数原型即接口。在具体的应用时,只要使用参数调用函数即可,而不用管具体函数是如何实现的,也就是说只要给定参数输入,函数就会给调用者一个输出值。


    比如,要计算Sin(26),若没有C标准库给我们提供的抽象(即接口),那么该如何计算呢?


    从本质上来讲,计算机只能做加法和移位(然后在此基础上,可以把减法、乘除法转化为加法和移位),因此首先应该建立数学模型,把sin(x)函数转化为计算机能执行的加减乘除。如果你没学过高等数学或微积分,那么问题就来了,你首先必须学会极限,然后是导数、连续、微分、微分中值定理,最后还必须掌握微分学的倚天宝剑----Taylor公式。
    下面是sin x函数的Taylor展开公式:

    -------------------sin x = x - x^3 / 3! + x^5 / 5! - x^7 / 7! + ......--------------------------

    在具体的实现过程中还涉及到算法,精度,浮点数的计算速度等很多问题。
    从上我们发现,没有抽象,面对的将是无穷无尽的细节,即便你考虑很周全,你的程序也可能还有Bug。


    而如果有标准库,我们只要知道函数原型 double sin(double x),然后double y = sin(26.0)即可,非常简洁。




    第二次接触到抽象这一概念是还是在C Primer Plus的最后一章“高级数据表示”,这部分内容是非常重要的,作者深入浅出地讲解了ADT,解释了数据隐藏、封装以及接口等重要的概念,使读者可以很顺利地过渡到数据结构和C++的学习。


    当然随着计算机的学习,接触到的抽象越来越多,对计算机的理解也会更加深刻!
    ----------------------------------------------------------------------------------------------------

    本章提出了文件、虚拟存储器、指令级结构、进程、虚拟机等抽象,而本书的一个隐含主题就是对这些抽象进行详细的介绍。


    作者从K&R的经典程序’‘Hello,world’‘开始:
    #include stdio.h
    int main(void)
    {
    ------printf("hello, worldn");
    ------return 0;
    }
    讲解了这个程序整个生命周期中计算机系统内所发生的一系列事情,为我们展示了一次奇妙的计算机漫游。


    (1)、使用编辑器创建文本文件hello.c,并将其保存在磁盘上;
    (2)、预处理器将hello.c文件修改为hello.i文本文件,以下是预处理过程:
    ----------删除“#define”,对宏进行展开;
    ----------对条件预编译指令“#if”、“#ifdef”、“#else”、“#endif”等进行处理;
    ----------处理“#include”,将stdio.h等文件直接插入到程序文本中;
    ----------删除注释“//”、“/* */”;
    ----------添加行号和文件名标识;
    ----------保留#pragma编译器指令。
    (3)、编译器将hello.i文件编译成hello.s汇编程序,编译过程很复杂,有一系列我完全搞不懂的过程,如:词法分析、语法分析、语义分析、中间语言生成、目标代码生成与优化等等,我想这部分内容应该属于《编译原理》中的内容了。
    (4)、汇编器将hello.s文件按照机器指令对照表一一翻译成二进制文件hello.o;
    (5)、链接器生成可执行目标程序hello,这个过程也是非常复杂,至少现在俺还不想去了解其中的细节(属于本书第7章的内容),这也是抽象给我们带来的好处。
    (6)、在shell命令行解释器中输入./hello,将会运行以上的“hello,world”程序,让我们再详细看一下具体过程:
    ----------shell程序执行,输出提示符,等待我们输入命令;
    ----------从键盘输入“./hello”(命令行的第一个参数必须是command或可执行文件,这里显然是当前目录下的可执行文件);
    ----------shell将hello文件从磁盘复制到主存;
    ----------操作系统将进程shell切换到hello进程,控制权交给hello;
    ----------执行main程序中的机器指令;
    ----------指令将“hello,worldn”从主存复制到寄存器文件,再从寄存器文件复制到显示器,最终显示在屏幕上。


    在(6)这一步中,涉及到高速缓存、进程、操作系统、存储器层次结构、虚拟存储器等很多重要的概念,在本书的后面将一一进行讨论。
    下面开始【CS:APP】第2章 信息的表示和处理
    本章的亮点在于公式的数学推导与证明!
    ------------------------------------------------------------------------------


    简单地讲,C语言就是用函数(算法)对 数据/结构 进行处理。
    所以这一章研究了三种最重要的数字表示和处理,即:unsigned,
    two*s-complement,floating-point。


    首先作为程序员,对二进制和十六进制之间的转换应该非常熟悉,一个简单的方法就是记住A---1010,C---1100,F---1111.


    书本练习题2.3之后,给出了Perl语言十进制和十六进制间的转换,下面给出C语言版:
    ----------------------------------------------------
    #include stdio.h
    #include stdlib.h //因为要使用atoi()函数


    int main(int argc, char * argv[])
    {
    ------int i;
    ------for (i = 1; i  argc; i++) {
    ------------int val = atoi(argv);
    ------------printf("%#0x = %dn", val, val);
    ------}


    ------return 0;
    }
    ------------------------------------------------------
    说明:较大数值之间的转换用计算机或计算器会更方便!
    以上程序将命令行参数的十进制表示为十六进制,下面的程序是将十六进制转换为十进制:
    ----------------------------------------------------
    #include stdio.h
    #include stdlib.h //因为要使用strtol()函数


    int main(int argc, char * argv[])
    {
    ------int i;
    ------for (i = 1; i  argc; i++) {
    ------------char *end;
    ------------long val = strtol(argv, &end, 16);
    ------------printf("%#0x = %ldn", val, val);
    ------}


    ------return 0;
    }
    ------------------------------------------------------
    说明:strtol()函数原型如下:
    long strtol(const char *nptr, char ** endptr, int base);
    其中nptr是一个指向需要转换的字符串指针,endptr是指向标志输入数字的结束字符串的指针的地址,base是数字的基数。


    # gcc -o test test.c
    # ./test 64 1f4 2ef
    程序输出如下:
    0x64 = 100
    0x1f4 = 500
    0x2ef = 751
    在书本28页给出了一个show_bytes()函数:
    -----------------------------------------------------
    typedef unsigned char *byte_pointer;


    void show_bytes(byte_pointer start, int len)
    {
    ------int i;
    ------for (i = 0; i  len; i++)
    ------------printf(" %.2x", start);
    ------printf("n");
    }
    -------------------------------------------------------
    这里要注意,因为start指针是指向一个字节序列,所以show_bytes()函数将打印出每个以十六进制表示的字节。


    该程序用以说明字节顺序是使用“大端表示”还是“小端表示”对于系统级编程是完全可见的,当然,在一般情况下,机器所使用的字节顺序是完全不可见的。


    另外,typedef和#define 是很有用的,尤其是在Windows程序设计中,这会很大地提高程序的可读性,比如:
    int x = 16;
    int y = 28;
    就不如下面程序的可读性强:
    typedef int height;
    typedef int weight;
    height x = 16;
    weight y = 28;


    27页作者给出了“端”(endian)的起源,很有意思


    George Boole给出了布尔代数,而Claude Shannon首先建立了布尔代数和数字逻辑之间的联系,这对计算机的发展至关重要,因为计算机本质上就是运用继电器(晶体管)网络来进行数字电路的布尔代数运算。


    布尔运算|、&和~形成了一个布尔代数;而^、&和~形成布尔环。环首先是一个加群(Abel群或交换群),因此对于加法运算^,每个元素应该存在逆元,显然,
    a ^ a = 0,也就是说每个元素的加法逆元是其本身。


    练习题2.10给出了 a ^ a = 0 及 ^ 运算可交换结合的一个应用:
    当然,不引进变量交换两数也可以这样做:
    void swap(int &x, int &y) //C++中的引用,也可用C指针
    {
    ------x = x + y;
    ------y = x - y;
    ------x = x - y;
    }
    作者强调,这样做并没有性能上的优势,只是一个智力游戏。


    接下来的练习题2.11是对上一练习题中inplace_swap()函数的一个应用,这里强迫大家在编程过程中要注意细节和特殊情况。


    练习2.12和2.13特别强调了对C语言中的位级运算|、&、~、^的熟练掌握和灵活应用,其中一个重要的应用就是实现掩码运算。


    练习题2.13比较灵活,要求理解集合中对称差Symmetric Difference、异或Exclusive_OR之间的关系:
    对称差是集合间的运算,两个集合A和B,其对称差AΔB有几种等价的定义方式:

    AΔB = (A - B)∪(B - A)
    AΔB = (A∪B) - (A∩B)
    或者用 XOR 运算表示:
    A Δ B = { x : (x ∈A) XOR (x ∈B) }.
    对称差可以在任意布尔代数中定义,写作
    x Δ y = (x ∨ y) ∧ ¬(x ∧ y) = (x ∧ ¬y) ∨ (y ∧ ¬x).


    由上分析知:
    x ^ y = (x & ~y) | (y & ~x);
    所以得到:
    int bool_xor(int x, int y)
    {
    ------int result = bis(bic(x, y), bic(y, x));
    ------return result;
    }


    楼主 2015-07-15 13:45 回复

共有回帖数 0
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知