那个C++写的网络数据包捕获分析的程序终于告一段落了。因为学校明年的教学安排有Java程序设计,再加上去年C++的教学方式让我到现在还心有余悸,所以我觉得还是提前翻翻Java的基础知识比较好,免得到时候再一次被动。

C++虽然只是学了一点点皮毛,但是OOP的基础概念之类的东西还是知道一些的。我在看Java基础的同时依旧在复习C++。这两种语言实现OOP的方式不尽相同,这样一边比较着一边辩证学习倒也颇有乐趣。

相较于Java,窃以为C++确实在对OOP的支持上确实略有逊色。我还是坚持自己以前的看法,即C++是大杂烩式的语言,以至于很难把C++归到哪一类。至于C++和OOP的关系,窃以为只能说是C++提供了一些特性,这些特性在一定程度上支持面向对象编程罢了,而不能说C++是一门OOP语言。C++的确是一门没有完全成熟的语言,很多所谓熟悉C++的人不过是在用C++的语法在写C语言程序罢了。就像Linus说的那样,“C++是一门糟糕的(horrible)语言。而且因为有大量不够标准的程序员在使用而使情况更糟,以至于极容易产生彻头彻尾的垃圾(total and utter crap)”。

我这么说并不是我不喜欢C++而找茬,相反,我个人很喜欢C++,但是我不掩盖C++的确存在的不足。我向来不认同说某种语言比某种语言好的论述,因为这要分情况来研究。假如语言是工具,我们总不能说榔头总是胜过锯子吧?这得分情况讨论,不是吗?

好了,不谈这些会挨喷的话题了,毕竟对技术的看法都是个在人学习和理解的基础上产生的,不同的环境和不同的学习历程就会有不一样的看法。我们不可能强求别人认可自己的观点,也没有必要非得去争个脸红脖子粗,没意义的,看法也不过就是看法么。

上面的话各位看官批判着去看就好了,貌似我从很早就学会了审视的去看别人的文章,有时也刻意的去“找找茬”。不过我们真应该学会批判着去看待技术类文章,和作者意见相同或者相左没有什么大不了的,学会自己思考才是最关键的。我喜欢那种独立思考之后再和别人的思维碰撞的感觉,方法上孰优孰劣倒是退居其次,重要的是通过这种方式,自己能学到新的思路和方法。

毕竟年关将近,只有零散的时间去研究技术。我们就不谈具体的技术细节了,来说说我最近做项目的一些感慨吧。再次声明,我毕竟在这一行阅历有限,谈到的不过是自己这两年学习的感触和一些方法的总结。这些方法不见得适用于任何人,也不见得每个人都能认同我的看法。不过即使意见相左,您也没必要非得和我争个青红皂白不可,您批判着看就可以,哪怕是全盘否定都行,我向来也不愿意在这些问题上浪费时间。

Read More

最近在读《深入理解计算机系统》(顺便打个广告,卡内基梅隆出品的这本书绝对精品),在书的48页提到了在C语言标准库limits.h中将int类型的最小值INT_MIN定义为-INT_MAX-1。书中提到了为何不写做-2147483648或者0x80000000,但是并没有给出解释,只是说这需要我们钻研C语言标准的一些比较隐晦的角落。

我们先看看MSVC的相关头文件内容:

GCC也给出了类似的定义方式:

为何是这样呢?这个头文件对我来说并不陌生。这几个宏也时常在用着,但是从没有注意过这个细节问题。百度一番后没找到满意的答案,最后勉强找到了http://www.hardtoc.com/archives/119这个比较靠谱的答案。因为英文看起来比较麻烦,再者作者描述的也不是很清楚,我写篇文章 权当翻译 一下,再加上一点点自己查到的内容。

我们先来查看C语言标准文档是怎么解释常量的,ISO/ANSI C99 和C11对于这里的描述是相同的。我们摘录下来:

“An integer constant begins with a digit, but has no period or exponent part. It may have a prefix that specifies its base and a suffix that specifies its type.”

大概意思是“一个常量起始于一个数字,但是不包含小数点或指数的部分。并且可能有一个用于指定数字基底的前缀和一个指定类型的后缀。”

Read More

§第六章 存储器层次结构

  1. 在简单模型中,存储器系统是一个线性的字节数组,而CPU能够在一个常数时间内访问每个存储器位置。实际上,存储器系统(memory system)是一个具有不同容量、成本和访问时间的存储器层次结构。CPU寄存器保存着最常用的数据。靠近CPU的小的、快速的高速缓冲存储器(cache memory)作为一部分存储在相对慢速的主存储器(main memory)中的数据和指令的缓冲区域。主存暂时存放存储在容量较大、慢速磁盘上的数据,而这些磁盘又常常作为存储在通过网络连接的其它机器的磁盘上的数据的缓冲地带。

  2. 如果程序所需的数据存储在CPU寄存器中,那么在指令的执行期间,在0个周期内就能访问到它们。如果在高速缓冲存储器内,需要130个周期。如果存储在主存中,需要50200个周期。而如果在磁盘上,则需要大约几千万个周期。

3.存储器层次结构围绕着计算机程序的一个称为局部性(locality)的基本属性。具有良好局部性的程序倾向于一次又一次的访问相同的数据项集合,或是倾向于访问邻近的数据项集合。局部性通常有两种不同的形式:时间局部性(temporal locality)和空间局部性(spatial locality)。

  1. 由于历史原因,虽然ROM中有的类型既可以读又可以写,但是整体上还是叫做只读存取器(Read-Only Memory,ROM),存储在ROM中的 程序常常被称为固件(firmware)。

  2. 理解存储器层次结构本质的程序员能够利用这些知识编写出更有效的程序,无论具体的存储器系统是怎样实现的。特别地,我们推荐以下技术:1)将注意力集中在内循环上,大部分计算和存储器访问都发生在这里。2)通过按照数据对象存储在存储器中的顺序、以步长为1来读取数据,从而使程序的空间局部性最大。3)一旦程序中读入了一个数据对象,就尽可能多的使用它,从而使程序中的时间局部性最大。

Read More

§第三章 程序的机器级表示

  1. GCC以汇编代码的形式产生输出,汇编代码是机器代码的文本表示,给出程序中的每一条指令。然后GCC调用汇编器和链接器,从而根据汇编代码生成可执行的机器代码。

  2. 现代编译器的优化产生的代码至少与一个熟练的汇编语言程序员手工编写的代码一样的高效和简洁。用高级语言编写的程序可以在很多不同的机器上编译和执行,而汇编代码则是与特定机器密切相关的。

  3. 程序员学习汇编代码的需求随着时间的推移也发生了变化,开始时要求程序员能直接使用汇编语言编写程序,现在则要求他们能够阅读和理解编译器产生的代码。

  4. 对于机器级编程来说,其中两种抽象尤为重要。第一种是机器级程序的格式和行为,定义为指令集体系结构(Instruction set architecture,ISA)它定义了处理器状态,指令的格式,以及每条指令对状态的改变。大多数ISA,包括IA32和x86_64,将程序的行为描述成好像每条指令按顺序执行的,一条信息结束后,下一条再开始。处理器的硬件远比描述的精细复杂,它们并发地执行许多指令,但是可以采取措施保证整体行为与ISA指定的顺序执行完全一致。第二种抽象是,机器级程序使用的存储器地址是虚拟地址,提供的存储器模型看上去像是一个非常大的字节数组。储存器系统的实现实际上是将多个硬件存储器和操作系统软件组合起来的。

  5. 虽然C语言提供了一种模型,可以在存储器中声明和分配各种数据类型的对象,但是机器代码只是简单的把存储器看成一个很大的、按字节寻址的数组。C语言中的聚合数据类型,例如数组和结构,在机器代码中用连续的一组字节来表示。即使是标量数据类型,汇编代码也不区分有符号数或无符号整数,不区分各种类型的指针,甚至不区分指针和整数。

  6. 虽然IA32的32位地址可以寻址4GB的地址范围,但是通常一个程序只会访问几兆字节。操作系统负责管理虚拟地址空间,将虚拟地址翻译成实际处理器储存器(processor memory)中的物理地址。

  7. 由于是从16位体系结构扩展成32位的,Intel用术语“字”(word)表示16位数据类型。因此,称32位数为“双字”(double words),称64位数为“四字”(quad words)。

Read More