Archive for February, 2009

飘过的日子飘过的歌

Saturday, February 28th, 2009

日子飘过留下了什么
我的记忆里却只有歌
这一程风雨陪我走过

(单击标题链接查看完整内容)

C++简明基础教程 – 第三篇 C++ in a Nutshell(解释部分)

Saturday, February 28th, 2009

这是本篇的第三部分。第一部分是代码在这里,第二部分是测试用例在这里,这里是代码的解释部分。

首先从直观上看代码,每个字符都是等宽的,字体是Courier New,打代码要习惯这种字体。颜色上,有三种,黑色、绿色、蓝色。黑色的包含了很多内容,但都不特殊。绿色的是注释(Comment),没有实际执行意义,是给人看的不是给机器看的。蓝色的一些是预编译命令,另一些是关键字(Keyword),也是保留字(ReservedWord),就是保留有固定的意义,不能用作其它用途。

C++中的注释有两种,你也看到了,两个斜线(“//”)表示从这里开始到这行末尾所有文字都是注释,而另一种是以“/*”开始以“*/”结束,表示它们中间的文字都是注释。后者来源于C语言。

预编译命令是辅助代码编译的前期阶段处理代码的命令。代码编译成可执行程序经过三个阶段,第一阶段经过预处理器(Preprocessor)初步处理代码,第二阶段将每个源文件独立编译(Compile)成对象(Object)文件,第三阶段将对象文件链接(Link)到一起构成可执行程序。预编译命令就是用作第一阶段的。

#include命令是C++中最常用也几乎是唯一能用到的预编译命令,它表示将某个头文件(Header)包含进来,从而使包含它的代码可以使用头文件提供的各种东西。<iostream>头文件提供了C++的输入输出流机制。流(Stream)是C++抽象出来的一种机制,可以把它想象成一个“槽”,扔到“槽”里的东西会输出,同样也可以从“槽”里获取输入。

C++提供了三个标准的流:cin、cout和cerr,它们分别代表标准输入流、标准输出流、标准错误输出流。对流进行操作的时候,我们一般使用“>>”和“<<”两个操作符(Operator),前者代表从流中获取数据,后者代表将数据输出到流。可以通过箭头的方向来判断数据的流向,从而理解它们的意义。

标准输入输出流是针对用户的命令行窗口的,<fstream>则提供了针对文件的流操作。输入文件流用ifstream声明,输出文件流用ofstream声明。类似地,可以把一个字符串当作一个流,<sstream>提供了这种支持,我们在代码中只用到了istringstream,还有ostringstream是向字符串做流输出用的。流跟其他的量一样使用前都需要声明(Declaration)。

这里不妨说明一下“using namespace std;”,这一行表示的是引用命名空间std。命名空间机制可以解决同一个域(Scope)内名称重复的问题,具体的设计思想不在“基础”范围内。这里只是需要说明像cin、cout这些流以及ifstream、ofstream这些流的类型都是包含在std这个命名空间里的,所以要使用它们必须要using这个std。如果不using的话,cin就应当写成“std::cin”来表明我们用的是这个cin而不是某个别的什么甚至没有定义的东西。

代码中出现了C++中常用的也是很基础的关键字,包括class public protected private virtual const inline void bool char int if else while for break return true false。下面分组简要说明,更详细的需要通过阅读代码(不止是这个代码,还有本系列以后的代码)来自己体会理解。

首先是void bool char int,先不看void,后三个都是数据类型,分别表示布尔型、单字符型、整型。C++中常用的还有short(短整数) long(长整数) float(单精度浮点数) double(双精度浮点数)以及unsigned short、unsigned int、unsigned long,它们是short、int、long三种整数类型所对应的无符号(unsigned)类型,也就是占用同样大的存储空间,但是只表示非负数。数据类型可以作为函数的返回值,而void表示函数没有返回值。这里所说的函数,跟数学意义上的函数是一样的,就是给它一些参数作为输入,它就返回数据作为输出(返回值)。而返回值用到另一个关键字就是return。

函数的一般声明格式是“数据类型 函数名 参数列表”。所以像“int main(int argc, char** argv)”就是一个相当标准的函数声明的例子,其中main是函数的名字,argc和argv是两个参数的名字。main函数也是C++程序一个很特殊的函数,因为它是整个程序执行的入口,也就是程序最开始运行的地方就是main。main函数的参数列表也是约定好的,argc表示从命令行运行该程序所提供的参数个数,argv则是一个字符串的数组,数组的每一项是对应的参数。比如我们的代码保存为cpptut3.cpp,编译后生成cpptut3.exe文件,命令行下进行测试时我们输入的是“cpptut3 tc1”,这里cpptut3就是第一个参数,tc2就是第二个参数。注意,第一个参数一般都是程序本身的名字,所以虽然我们只需要输入文件名tc1,但还是会有两个参数传进来。

从这里接着说下去,我想你应该就明白 if 这个关键字的作用了。顾名思义,它用来判断某件事情是否成立,然后决定做什么。else 关键字配合 if 使用,它说明了如果表达式(Expression)的值为假(false),也就是不成立,程序应该做什么。而到底做什么,则是另一些语句(Statement)组合起来形成的一个块(Block)。C++中用一对花括号({})来标明一个块,一个块等价地构成一个域(Scope)。另外,C++中的语句末尾都有一个分号“;”。

if条件语句用来判断真假并执行一次块的内容,while则不断地根据条件真假循环地执行块的内容。如果不想循环了,还可以用break语句跳出循环,执行while以后的东西。另外还可以用continue语句结束本轮循环(这个代码里没用到)。if和while很相似,它们都有相同的语义结构:“if(…){…}else{…}”表示“如果…的值是真(true)就执行…否则执行…”,而“while(…){…}”表示“当/只要…的值是真就执行…”。else子句加了斜体,因为它并不是必须的。

除了最原始的while循环语句,C++中还有一个很强大的for循环语句。for语句的结构是“for(初始化; 循环条件; 增量){执行体}”。比如我们想输出100以内的所有偶数,我们可以从0开始每次递增2并输出这个值,用for语句就写成“for(int i = 0; i <= 100; i += 2) { cout << i << endl; }”。其中i是for语句域内的变量,所以声明int i放在for语句的初始化部分;循环条件是i<=100;增量部分我们用到了C++的简化操作符“+=”,相当于写成“i = i + 2”;循环体我们向cout流输出i的值,同时还用endl输出一个换行符。代码中的for语句例子更加复杂,需要好好体会,弄清楚循环的真正含义。

下面我们来看class 以及 public protected private这四个关键字,它们都是类定义中很常用的关键字。所谓的类,就是某些具有相同特征的东西抽象出来的一个统一类型。代码例子中我们写了一个类App,用来对我们需要实现的程序进行包装。我们会在以后的篇目里进一步讨论这个类本身,这里只是关注形式上的东西。

首先,类声明是一个语句,所以最后的}还要跟一个;才对。写一个类与写一个函数一样分为声明和定义两部分,声明的大体框架是“class 类名{类声明体};”。类声明体中我们声明类的方法(Method)和成员(Member)。方法就是写在类里面的函数, 比如App::App App::~App App::run App::process App::print App::log。成员就是写在类里面的变量或者常量,比如in_ out_ data_。无论方法还是成员,都需要限定谁能访问它,于是我们用到了public protected private三个访问限定符(Access Specifer)。用public限定的东西,在类的内外都可以用;用protected限定的东西,类里面可以用,从类派生的子类(SubClass)也可以用;用private限定的东西,只能在类本身里使用。关于子类,以后的篇目会详细说明;virtual关键字也与此有关,暂略。而const则表示方法不会改变类成员的值;需要注意const书写的位置。

类的定义一般与声明分开,比如App::run App::process等等,都是像一般的函数一样定义。而App::log我们却写在类的声明里面,我们称之为内联(Inline)方法。同时我们还使用了关键字inline说明它是一个内联方法。函数也可以内联,声明的方法跟内联方法相同。内联适合一些很小规模的内容,编译器在编译的时候会把它们展开应用到每个使用到它的地方。但这也是不一定的。关于内联的知识很复杂,不在“基础”范围内,这里用到只是为了指出这个语言现象。

我们眼前的类一定有两个方法让你觉得特别刺眼,那就是App::App和App::~App。因为C++中的名字首先不能重复,其次名字只能是“字母或下划线开头的字母、数字与下划线组合”。但这两个方法确实是合法的,因为它们特殊。App::App叫做构造函数(Constructor),App::~App叫做析构函数(Destructor)。从它们的名字就可以大概理解它们的作用了。

构造函数在用一个类创建一个新的对象时被调用,应用到这个对象,负责对象的初始化构建工作。析构函数则在这个对象超出其作用域时被调用,负责对象的清理工作。现在可以看主函数main中的“App myApp(argv[1]);”这一行,这里我们就用类App创建了一个叫做myApp的对象,另外我们还给构造函数提供了一个参数argv[1]。创建对象后我们就可以使用它了,“myApp.run();”就调用了run函数。而这一切都是在“int main(…){…}”这个域中工作的。

C++中的任何东西都有一个使用范围,这个范围用域来结构化。当前域可以使用域外声明的东西,当前域可以使用域内已经声明的东西,当前域内声明的东西会覆盖掉域外相同名称的东西。理解好这三条,就是一个很好的开始。

回到之前的问题。当超出了main函数定义的域后,对象myApp就超出了它的作用域,此时就会对myApp调用析构函数。为了更好理解构造函数和析构函数,我们不妨看看它们到底都做了些什么:

(代码需要查看永久链接页面才能看到)

综合上面的代码和注释,一个很显然的执行过程就出现了。而在打开、关闭文件之间,我们的run()函数被调用来进行一些操作。我们把输入文件按行处理,line是一个字符串(String)表示当前读取的行,line_count则是行号计数。我们用getline函数读入一行内容到line中。注意,我们没用“in_ >> line; ”是因为默认情况下这样读取字符串只是到一个空白符为止,而我们需要处理的文件的每一行是有很多空格的,所以这样会导致错误。

process()函数处理一行命令。我们允许的命令格式已经在那个C风格的注释中简要说明了,结合测试用例我想你应该能明白。process()函数的大体框架是好几个互斥的if条件语句,用来区分对不同的命令进行处理。在每次处理的最后,如果发生错误会返回false到run()函数的执行流程里,示意终止对输入文件的处理,如果顺利执行,则调用print()函数打印(指向控制台输出)被操作的数组(Array)的值。

整个程序处理的核心对象是很多个数组,我们允许为每个数组命名,并通过这个名字来对相应的数组进行操作。这听上去是个很强大却又很复杂的东西,但我们只用了不到二百行就实现了。如此高效的原因在于我们使用了STL(Standard Template Library)。关于STL的描述我们将在下一篇中进行。

好了,到此为止对这段代码的说明应该差不多了,具体的执行流程不在语法范围内,需要你好好阅读,仔细思考。

(本篇完;做人要厚道,转载请注明出处)

VMware演示手机虚拟化

Friday, February 27th, 2009

http://news.csdn.net/n/20090226/123601.html

微软演示新技术:空中书写

Friday, February 27th, 2009

http://news.csdn.net/n/20090226/123604.html

C++简明基础教程 – 第三篇 C++ in a Nutshell(测试用例部分)

Friday, February 27th, 2009

这是本篇的第二部分。第一部分是代码在这里,第三部分是代码解释在这里,这里是测试用例。

——————– tc1.txt ——————–

sort ary_not_def

——————– tc2.txt ——————–

array ary1       4 34  9  2 1
sort  ary1
array ary1 0

——————– tc3.txt ——————–

array arr 3 4 1 1 2 3 4
size arr
access arr 100

——————– tc4.txt ——————–

array a 93 8 21  32 21  10  84  0  0 0   0 0  283  4  2 1 3
size a
access a 0
sort a
array b 0 0 0 0 0 1 2 3 4 8 10 21 21 32 84 93 283
sort b

在命令行下运行的情况:

——————– console.txt ——————–
C:UsersjxDocumentscpptut3>cpptut3
*** error: need argv[1] for filename!

C:UsersjxDocumentscpptut3>cpptut3 tc1
*** error: array doesn’t exist!
*** error detected at line 1

C:UsersjxDocumentscpptut3>cpptut3 tc2
array ary1 = [4, 34, 9, 2, 1]
array ary1 = [1, 2, 4, 9, 34]
*** error: array already defined!
*** error detected at line 3

C:UsersjxDocumentscpptut3>cpptut3 tc3
array arr = [3, 4, 1, 1, 2, 3, 4]
size of array arr is 7
array arr = [3, 4, 1, 1, 2, 3, 4]
*** error: index out of range!
*** error detected at line 3

C:UsersjxDocumentscpptut3>cpptut3 tc4
array a = [93, 8, 21, 32, 21, 10, 84, 0, 0, 0, 0, 0, 283, 4, 2, 1, 3]
size of array a is 17
array a = [93, 8, 21, 32, 21, 10, 84, 0, 0, 0, 0, 0, 283, 4, 2, 1, 3]
a[0] = 93
array a = [93, 8, 21, 32, 21, 10, 84, 0, 0, 0, 0, 0, 283, 4, 2, 1, 3]
array a = [0, 0, 0, 0, 0, 1, 2, 3, 4, 8, 10, 21, 21, 32, 84, 93, 283]
array b = [0, 0, 0, 0, 0, 1, 2, 3, 4, 8, 10, 21, 21, 32, 84, 93, 283]
array b = [0, 0, 0, 0, 0, 1, 2, 3, 4, 8, 10, 21, 21, 32, 84, 93, 283]

运行后输出的日志文件:

——————– tc1.log ——————–

(空白)

——————– tc2.log ——————–

array ary1 defined
array ary1 sorted

——————– tc3.log ——————–

array arr defined

——————– tc4.log ——————–

array a defined
array a sorted
array b defined
array b sorted

C++简明基础教程 – 第三篇 C++ in a Nutshell(代码部分)

Friday, February 27th, 2009

本来想按照传统细细说明C++的每个语法规则,但是又感觉太繁琐,根本做不到简明。于是突发奇想,写了下面的例子程序作为主要内容。先看一遍程序,然后观察一下测试用例,等我写完本篇的解释部分再具体研究程序的每个细节。注意,这是非常重要的一段程序(也挺长的),需要有耐心,它包含了C++中所有的我认为比较基础的语法。

为了在首页显示美观,代码设为隐藏,需要单击标题链接才能看到代码。你的浏览器需要对JavaScript和CSS有良好的支持。

C++简明基础教程 – 第二篇 编程语言学习引论

Wednesday, February 25th, 2009

编程,即英文Programming,正译为程序设计。编程更简明,故取之。

语言,即英文Language。代码如诗,编程语言也是人类表达思想的一种方式。

学习引论,就是包含了为学习做铺垫的引导性知识的一篇不太全面的文章。

C++不是中国人发明的,出现在很久以前,用英文、数字以及各种符号书写,是长期以来广为使用非常强大的工业语言。因为以C语言为基础,又较之更近好几步,所以叫C++。个人观点,第一个加号代表代码长度,第二个加号代表代码编译后的长度。

代码,就是你用C++写出来的东西。代码虽然如诗,但少数是可以当诗一样读的,多数只是用来计算的。计算机负责计算过程,程序负责控制这个过程。

程序,即英文Program,是一个笼统的概念,使用程序需要运行。

运行,即英文Run,又称执行,即英文Execute。程序运行之前需要编译。

编译,从代码到可执行程序的过程。代码编译后产生二进制串。

二进制,是数字表示的一种形式,写出来只有0和/或1。

编程中常用的进制有二进制(Bin)、八进制(Oct)、十进制(Dec)、十六进制(Hex)。括号中是它们的英文简写,一般也更加简化为B、O、D、H四个字母。

二进制是计算机表示数据的根本形式。学习编程需要对二进制运算了如指掌。

二进制表示中的每一个0或1称为位(Bit),音译为比特。八个比特称为一个字节(Byte),两个字节称为一个字(Word),两个字称为一个双字(Dword),两个双字称为一个四字(Qword)。

回到代码的问题上来。代码(Code)又叫做源(Source)程序,有时又简称为程序或Source,所以写程序和写代码是一回事,源代码和源程序也是一个东西。源程序的逻辑承载介质叫源文件(物理承载介质是存储设备)。具有相关性的源文件与其他资源(Resource)文件组织在一起叫工程(Project)。具有相关性的工程与文档(Documentation)组织在一起叫解决方案(Solution)。

在程序员的世界里,英文简写是一种非常自然的符号。一般感觉上比较长或难记的单词,都会记成简写形式。简写没有固定规则,以习惯和易用为主。一般的简写规律是去掉元音字母aeiou和略读的辅音字母,或者这样写感觉不好看的就截取前几个字母构成主要发音部分。所以有了下面的简写:src(source),rc或res(resource),prj(project),doc(documentation),sol(solution)。Code不用简写,它应该比Coke好看。

回到编程的问题上来。编程的核心目的是解决问题。数学家认为世间万物都可以用数学来描述。基于此想法,编程可以解决世界上所有问题,解决基础是数学运算。所以我们的机器叫做计算机。在目前的情况下,你在本教程里看到的多数还是纯数学运算。从纯数学运算出发解决实际问题的过程叫做建模,这是一个非常深刻而玄妙的领域,远超出本教程范围。

程序指导的是数学运算,所以必然要包含数和算两个部分。

数,即数据(Data),是数学运算的受体。数据是一个很宽泛的概念,我们将它狭义地看成量(Quantity)的组合。量,分为标量(Scalar)和向量(Vector)。标量又称纯量或无向量。向量又称矢量,具有多个分量(Component),每个分量可以是标量或者向量。向量的层次展开我们称为数组(Array)。量,还可以分为常量(Constant)和变量(Variable)。常量的值(Value)在计算过程中保持不变。变量又称变元,值可以改变。量的有机组合形成了结构(Structure)。结构便于我们组织上层数据,操作底层的量。

算,即计算(Calculate),是数学运算的主体。计算是一个很抽象的概念,如果从你的数学常识来看,包括了加减乘除等普通运算,还有操作性的过程,比如循环。计算更多地体现在解决问题的过程性。

数和算被具体化为数据结构(Data Structure)和算法(Algorithm);学习编程只有一个公式:“程序 = 数据结构 + 算法”。

以上就是关于C++学习的引论,下面我们开始真正学习C++。

(本篇完;做人要厚道,转载请注明出处)