第3章 感受(一)——3.1. Hello world 经典版

[回到目录]
白话C++

第3章. 感受

Hello world!,Hello C++,我们来了!


3.1. Hello world 经典版

毫无疑义,一个真正的程序员,总是愿意把自己的第一次——我是说“感受”——献给是“Hello world”。

著名的C语言的教程书:《The C Programming Language》,作者是C语言的两位创始人(K&R),他们把“Hello world”作为第一个范例程序,从此这几乎成为道上的规矩了。

“Hello world”的目的之一,是通过简短的代码,让学习者以最小代价对“程序长什么样”有个整体认识。目的之二,往往也是用来检验编程环境是否正确,所以要学习本章,请您首先确保已经依据2.1节完成Code::Blocks的安装。

〖小提示〗:C之前,还有B

“Hello world”范例因C语言而成为编程学习的传统,但据说它并不是最初出现在C语言的教程书上,其实C语言的作者之一,早先在贝尔实验时写过内部技术文件 《Introduction to the Language B》时,就开始用上这个范例。作为一名程序员,了解语言的发展史,是一件很有意义的事,大家何不用这个小例子,作为一个回溯计算机编程语言史的起点呢?

3.1.1. 向导-控制台项目

Code::Blocks提供了“控制台项目向导”,可以方便地创建出一个控制台程序的“架子”,而这个“架子”的内容,其实就是“Hello world”程序。

〖小提示〗:Project,是“工程”,也是“项目”

一个程序,往往由多个源文件组成,为了方便管理这些源文件的编辑、编译、调试等过程,通常会用一个“项目”来管理它。

这里“项目”在英文时在,对应称为“Project”。不过像我,还有一些较早书籍,一直将其“Project”翻译为“工程”。这至少有两样好处:

其一,不会与“Item”这个常用词混淆;

其二,软件开发过程中管理存在“项目管理”(负责人为“项目经理”),这里面的“项目”,在英文里也是Project,可是,它跟我们这里的所指的项目,其实并不搭边。

本文考虑良久,仍然尊重当前国内比较广泛使用的作法,采用“项目”这个翻译,但在此提示各位了解其中内涵。

现在我们学习如何通过向导创建一个控制台应用,并且直接编译、运行它。

步骤1:Code::Blocks主菜单“文件”→“新建”→“项目…” 弹出如下对话框中,开始“Console Application”。

(图 7 开始“Console Application”向导)

步骤2:如果出现向导的“欢迎界面”,直接点击下一步。

步骤3:出现用于选择语言的对话框,选择“C++”,下一步。

步骤4:出现选择文件夹的对话框,在“项目标题”中输入“HelloWorld”。两单词连写,不包括双引号。本步操作结果将会在“我的文档”中“CodeBlocks Projects”目录下,新建一个名称为“HelloWorld”的目录。

步骤5:出现编译器选项对话框。在“编译器”中选择“GNU GCC Compiler”。其下则默认“Debug”与“Release”两个编译目标都是默认选中的,不必改变,通常我们都需要调试和发行两个版本,就算是一个Hello Word的简单程序。按下“完成”。

步骤6:必要时按下“Shift + F2”,出现“Management”侧边栏,如下图:

(图 8 项目管理器)

步骤7:双击上图所示的main.cpp,将打开该文件。暂时我们还并不需要修改它。

步骤8:主菜单“构建”→“构建”。或者,用热键Ctrl+F9,完成编译。

步骤9:再按Ctrl + F10,运行。(9、10步也可以通过工具栏实现,请读者自行熟悉)。下面是运行结果图:

步骤10:其中“Hello world!”是我们程序的输出。下面的内容是Code::Blocks为了方便我们调试而增加的内容(本来该程序运行后,就直接退出),包括显示了程序运行用时,以及提示“按任意键退出”。

3.1.2. 初识代码

参考上一小节步骤7、步骤8,进入代码编辑窗口,我们看到以下内容。为了描述方便,我们为它加上行号。

001 #include <iostream>

003 using namespace std;

005 int main()
006 {
007 cout << "Hello world!" << endl;
008 return 0;
009 }
  • 编译指示

001行代码是一句“编译指示”,它的具体含意,我们随后就讲。编译指示总是在新的一行中,以#开始,并且最后没有使用“分号”结束,比如:

#include <iostream>

编译指示是编译过程所需要的一些信息,它们影响一个程序是否能被正确的编译,并不在程序运行期造成影响。

  • 声明性语句

C++代码中,多数内容是语句,其中有一类语句在作用上完全等同“编译指示”:它们同样仅影响编译过程,在编译结果中并不生成对应的指令。仅因约定成俗,我们也把它们称为“C++语句”,本书则称之为“声明性语句”,以示区分。

本例中,003行是一句声明性语句:

using namespace std;

该句的具体含意,我们随后就讲。

这类语句的语法格式上,和普通语句没有明显区别。

  • 可执行语句

007和008是两行可执行的语句。

cout << "Hello world!" << endl;

return 0;

编译之后,这些语句会生成对应的计算机指令,用于程序执行。

  • 函数空壳

剩下没提到的代码,是一个“C++函数”的空架子:

005 int main()
006 {
007
008
009 }

int 和 main()的含义,我们随后讲到。之后是一对“花括号{}”,这是函数体必须的空架子。一个函数体中,通常会包含有一些语句,在本例子,就是007与008两行“可执行语句”。

  • 大小写区分

C++是一门区分大小字的编程语言。请特别注意本例子所有关键代码,都是小写字母。

3.1.3. 头文件

9行代码,其中两行为空行,纯粹为了排版上的美观,如何做到往控制台输出一行“Hello world!”呢?

很容易注意到第007行代码:

cout << "Hello world!" << endl;

这一行中,我们不认识“cout”和“endl”,还有两个 “<<”,它是两续的两个“小于号”组成的操作符。事实上,正是依赖它们,才把那句“Hello world!”输出屏幕。以我们的硬件知识,可能我们根本不知道如何往有关屏幕输出的硬件设备(比如显卡)输出内容,是cout、<<、 endl这些几个乐于助人的“工友”,帮助了我们。

这位“工友”来自哪里?当我们需要时,我们如何找到他们?生活中,当你需要一些你自己不擅长的事务,比如家电坏了需要维修、或者需要搬家,或者下水道堵了想要找人疏通,你是如何寻求帮助呢?

有人说:“通过贴在电梯里的小广告啊”,这样说也太有辱C++的神圣了。这里想引出的一个生活中的事物是“名片”。

“张速通” 身怀绝技,特别擅长疏通下水道,但如果他成天只闷在自己家里,那谁也不会找他。于是他印制了大量名片,名片上写明他的专长,及联系方式。有一天“丁爱堵” 上街时,也收到他发出的一张名片(说来说去怎么还是有点街头小广告)。再有一天“丁爱堵”家不幸发生下水道堵塞,于是他通过名片,找到了小张,最终实现“ 调用/Call”小张的功能来实现疏通下水道的任务。

我们说过,高级语言往往为我们提供了大量的现成的功能,这些功能往往存在一些代码库里。“cout、<<、endl”等等,正是来自C++的某个库。

当我们在C++程序要调用到某一现有的功能时,我们首先要确定这个功能放在哪个库里;但是光找到“库”还不够,放在“库”中的功能有一大堆,你要哪一个呢?这就需要“名片”了。

C++语言中的功能代码,通常以“函数”或“类”的形式提供。这 二者都需要“定义”。所谓“定义”者,就是相当于函数或类的“真身”。但是,“真身”往往只有一位,这时候就需要“声明”。所有“声明”,就是类似写上一 句话:“有一个函数或类,它叫什么名字,它大致能做什么事。你们谁要是需要我,就找我吧”。因此,我们把C++中的“声明”对应到生活中的“名片”。

那么,C++的“头文件”是什么呢?

C++语言中的“头文件”,你可以把它理解为一个“名片夹”,也就是说C++的头文件中,往往是若干个(函数或类)声明的集合。比如你想装修房子,于是装修公司给了您一个名片夹,那里面有这么些人的名片:“李粉刷”、“林电工”、“陈水管”、“吴防盗”……方便吧?

没错,头文件是一个“名片夹”,并且这些夹里名片所指征的功能,通常是有一定的关联的。

现在请看第一行代码:

#include <iostream>

  • <iostream>

iostream 就是一个头文件。名字中,i表示input/输入;o表示output/输出,结合stream/流,就是指这个头文件中,声明了大量和“输入输出流”有 关的功能。“cout、<<、endl”,就是这个“输入输出流”头文件中,声明的三位大侠。

iostream被<>包着。C++中,头文件可以被两种符号包住,另一种是双引号,比如:

#include “mystream.h”

二者的区别在于,尖括号表示这个头文件位于编译系统预先定义的目录内;而双引号表示该头文件位于程序作者自己定义的目录内。

iostream其实是C++标准库里的文件,当然属于“预定义”的目录。C++标准库在我们当初安装Code::Blocks时被自动安装了,它就在Code::Blocks的安装路径下的MinGW/include/c++/3.4.5/下。

 

〖现场作业〗:查找标准库目录

请大家亲自去查出iostream文件在您的机器上的安身之处。然后检查它的扩展名是什么。

通常C++头文件的扩展名可以是:.hxx,.h,.hpp等。其中仅有.h格式和C语言保持兼容。不过,标准库的头文件比较特殊,它们的扩展名就是没有扩展名。

  • #include

#include 是一个固定格式,当需要引用另外一个头文件,其语法格式就是在代码的新行处,写上:

#include <头文件>

或者

#include “头文件”

当然,学究一点的语法是没有中间的空格(我们并不推荐),比如:

#include<头文件>

 

〖重要〗:include 原则

include一个头文件,给当前源文件带来,只是增加了“声 明”内容。并不会真正把“定义”的内容“夹带”进来。然而,和生活的“名片”不同,C++的头文件还可以include其它头文件,层层引用,必然造成编 译速度的降低。因此我们的原则是,争取做到:“真用,才引用”。

嗯,说实在,我觉得自己办公桌上的一堆名片确实有些太乱了。

3.1.4. 标准输出cout

再次看007行代码:

cout << "Hello world!" << endl;

此时,我们知道cout、<<、endl。都是通过代码中第一行#include <iostream>引入,不过,它们具体都是什么含义呢?

  • cout

读成:C – out,音:“西,奥特” (我的英语老师不会来我这里学习编程,我确信!)

cout是C++库中一个对象,它代表“标准输出设备”,对于一台PC,标准输出通常就是它的屏幕。对于控制台程序,标准输出又特指当前的控制台窗口。

  • <<

<< 是一个操作符号,可以读成“流输出符”,或者简称为“输出符”。

什么叫“操作符”?像“加、减、乘、除”就是四个操作符。在 C++中,这四者分别用“+、-、*、/”表示。<< 虽然是两个连续尖括号(而不是书名号:“《”),但也仅表示一个“操作符”,之所以不是一个尖括号,那是因为“<”,难道不是小学生熟悉的“小于号 ”吗?

“减号(-)”是个一个二元操作符,表示它需要两个操作数。比如:3 - 2,表示将3减去2。

“流输出符(<<)”也是一个二元操作符,它实现的操作是,将右边的东西,输出(打印)到左边的东西上。通常,左边的东西,必须是一个“流”。

cout << “Hello world!”,表示将 ”Hello world!”这句话,输出(打印)到cout上去。

  • endl

endl 在C++标准库中,被定义为一个函数。它负责在屏幕上输出一个换行。

 

〖现场作业〗:学习使用endl

将007行改为:

cout << "Hello world!" << endl << “Hello universe!” << endl;

观察屏幕输出内容中的换行效果。

  • 小结

理解了“cout、<<、endl”;我想您一定看懂了007行代码了。

大家可以试着将“Hello world!”引号中的内容改为“Hello C++”,然后重新编译、运行。(请暂时不要改成汉字,有关汉字的问题,我们下一节详谈。)

〖危险〗:全角与半角字符

同样是“逗号(,)”,大家注意到了吗?中文输入法输入下的逗 号要比英文状态下的它的兄弟粗一些。更明显的还有双引号。通常我们中文状态下输入的字符,称为“全角字符”,而英的称为“半角”字符。 在上述代码中,包括英文字母、数字、空格(没错,如果您不小输入一个全角空格,可能会让你抓狂)、标点符号,都是半角字符。比如代码中Hello world!两端的引号。 通常,你所使用的输入法程序,会轻松地帮你完成全角与半角的切换,在Windows下,热键应该是“Ctrl + .”。

3.1.5. 名字空间

我们已经知道,cout和endl等都来自C++标准库的定义。cout是一个对象的名字,而endl是一个函数(严格说是模板函数)的名字。

C++中对某一物体的命名,和现实生活一样,需要尽量避免重名。假设我们公司新来一个家伙,他的名字也叫“南郁”的话,那么我肯定心里有些不爽。个人的不爽还是其次,主要同名会造成很多指代不清的麻烦。

然而重名有时是难免的。C++编程时也是如此。想象一个团队合写一个“小区‘四害’杀防管理系统”的软件。你负责用户操作界面模块,小丁负责“四害”数据定义模块。写着写着,你需要定义“鼠标”, 而他需要定义“耗子”,于是很可能就有重复的名字叫“mouse”了。

重名怎么办?如果新来的那个家伙是在市场部,而我在研发部,那么公司里人,可能在必要时,会称呼我为“研发部的南郁”,称他为“市场部的南郁”。

程序代码也是如何,可以加上一个“名字空间/namespace”,凡是在这个空间里的定义的事物,他们都拥有一个相同的“名字前缀”。

cout 和 endl其实都位于名字空间“std”之内——事实上,整个C++标准库,都位于std这个名字空间之中。

所以,cout完整的名字,其实是:std::cout。同样,endl其实是std::endl。即:前缀由名字空间加上“::”(两个连续的冒号)。既然如此,007行代码,完整版的写法是:

std::cout << "Hello world!" << std::endl;

C++允许我们用:using namespace XXX; 这条“声明性指定”,以减少编码时的打字量。这就是003行代码的作用:

using namespace std;

〖轻松一刻〗:你需要名字空间吗?

最近我的一位姓“李”的女同事,她先生姓“奚”,他们刚生个女儿,取名:“奚李花啦”。这样的名字我想是不需要什么名字空间了。不过有许许多多的人,在13亿的人群中,我们都需要“名字空间”,并且其实我们一直都拥有着不止一个的名字空间,以“小强”为例:

籍贯:某某省某某市::小强;

职务:总经理::小强

头衔:著名歌手::小强

物种:德国小蠊::小强

3.1.6. 函数

前面提过,C/C++中有很多功能代码,都是以“函数”的形式存在“库”中。(另一种重要形式是C++的“类”。)

其实不仅仅是“库”里面的代码,平常我们写一个程序,也会习惯把 一些相对固定的功能代码,写成“函数”的形式。特别是在以“面向过程”的思路写程序时,经常一个“过程”就对应一个“函数”。由于在现实中,我们(以“面 向过程”的思路)解决问题时,我们的习惯就是“大事化小,小事化了”。对应到(以“面向过程”的思路)写程序时,就是“大函数化小”,“小函数化了” 。

结果类型 函数名称 (函数参数列表)

{

——具体实现过程的代码

}

  • “函数名称”,通常就是您要做的事情的名称,或者动作的名称。
  • “函数参数列表”,是指做这件事时,您需要哪些数据。
  • “结果类型”,做完一件事以后,通常需要告诉做事者一个结果,但需要指定结果的类型。

C++语言定义一个“特别关键的单词”,用来在做事过程中,随时返回结果,这个“关键的词”,是:“return”。

平常生活中,大家天天都要吃饭吧?如果要用C++伪代码形式的“函数”来描述“吃饭”的话,那么,它的定义大致如下:

饱或不饱 吃 (饭)

{

____// 吃饭的具体实现;

____return 是否吃饱;

}

函数名称:吃。

函数参数:饭。

函数返回类型:吃饱了吗?

〖轻松一刻〗:吃饭是一件大事!

“吃饭”值得写成函数吗?这个问题我也很犹豫。吃,再简单不过的事了,似乎完全不值得写一个函数来封装……也许是我心理有阴影吧:小时候在饭桌上,每当我要说话,父母就会很严厉地批评:“吃饭比天还要大!不许说话!”

根据“大事化小,小事化了”的指导原则,我决定把“吃饭”函数先化解成以下小事:

  • “张嘴”
  • “嚼食”
  • “说话”(弥补我童年吃饭不能说话的心酸)
  • “下咽”

在这四件事中,我发现“嚼食”可以继续化解,所以它被列为一个新的函数,其它的,都“小事化了”。于是需要一个新函数:“嚼食”。为了简化代码,暂且假设我们不关心“嚼食”的结果(也就是说,不管嚼得烂不烂,我们都将下咽)。

无结果 嚼食 (饭

{

_____我嚼我嚼我嚼嚼嚼嚼嚼嚼……

}

最终“吃饭”函数长这样子:

饱或不饱 吃 (饭)

{

____张嘴;

____嚼食(饭);

____下咽;

____return 是否吃饱;

}

 

现在来进一步整理一下“函数”的作用:

第一、 学会写函数,我们就可以程序中自己写一些函数供自己使用。如果我们是几个人共同学一个软件的话,那么团队之间,大家互相调用彼此的函数,是一件再正常不过的事了。

第二、 有些功能,我们自己实现不了,这时就需要考虑从C++库中找找有没有现成的,比如一些数学上复杂的算法,我们可以通过C++标准库,或boost库中 math模块中查找。还有一些是操作系统才能完成的事,比如建立或删除一个文件夹,我们同样可以从相关的库中找到这些函数。

第三、 有一个特殊的函数,叫做“主函数/main function”。它是专门供操作系统“倒过来”调用的C++程序的函数,也称为程序的入口函数。

3.1.7. 主函数

请看005~009行的代码:

005 int main()
006 {
007 cout << "Hello world!" << endl;
008 return 0;
009 }

你应该认出来,这是一个函数。

〖现场作业〗:辨认函数定义

请针对以上代码,回答以下问题:

1、 函数名称、参数列表、结果类型各是什么?

2、 这个函数做了一件什么事?

3、 返回结果是什么?

4、 通过Google搜索int 在C++中表示什么数据类型

这个函数名为“main”,它就是“主函数”,或者“入口函数”。也就是C++程序中,专门等待操作系统调用的函数。

“等待操作系统调用……”,具体含意是?

想象我们写了一个程序,安装到用户的硬盘上,并且在其电脑桌面上创建了一个程序的快捷图标。

某年某月的某一天,用户把鼠标移动到那个图标上,图标的心扑通扑通地跳,终于用户双击图标了!图标觉得自己好幸福啊,因为用户的这个举动,其实就是在说:“操作系统啊我的大管家,我对这个图标所代表的程序有兴趣了,请你运行一下这个程序吧!”

操作系统接受这一事件的请求,于是它在磁盘上找到那个程序,然后开始启动这个程序。如何启动呢?

步骤1:把这个程序从电脑磁盘上搬到电脑内存里。(我管这个过程叫做程序的“投生”,磁盘是死的阴间,内存才是活的人生)

步骤2:找到事先约定的,这个程序的入口点。然后从这个入口开始执行程序里的一条条指令。(程序=指令的组合,你还记得吗?)

 

〖重要〗:“main函数”并非程序的第一件事!

有时候初学者会认为main函数就是C++程序运行时所做的第一件事。这是个误解。别忘了,程序启动时,首先做的是“投生”。“投生”其实是程序进入内存前后的一个初始化过程,在这个过程中,许多事情,就已经发生了……

虽然人类的活动或许应该从“亚当、夏娃”的出现作为起始点,但其实上帝在造人之前,这个世界已经过去5个工作日了。

主函数的“参数列表”形式,有两种版本,其一就这里所使用的:空,即不需要参数,另一种形式,我们后面课程中会讲到。

主函数的返回值是int。int在C++代表整数类型(integer)。在本例子,这个函数只是简单地,不管三七二十一返回0。在此处,0的意思,就是告诉操作系统:“平安无事,本程序寿终正寝”。

〖轻松一刻〗:“病毒程序”

操作系统对于电脑用户来说,像个“大管家”;对于程序来说,几乎就是“上帝”。多数程序都是上帝良民:接受上帝的管理。有一天,一个叫潘多拉的程序员觉得很无聊,他打开一个了盒子,于是这世上有了“病毒程序”。“病毒程序”最大的特征,就是想尽办法避开操作系统管理。

[回到目录]
白话C++

你可能感兴趣的:(编程,c,生活,语言,include,iostream)