}
}
//---------------------------------------------------------------------------
第三个文件:
assifunc.cpp用以存放辅助作用的函数,现在只有一个。
现在还差一个函数:
ToUpper()。
这个函数用来将用户输入的某个小写字母转换为大写。
当然,如果用户输入的不是小写字母,那就不用转换。
和上面的两个函数不同,它需要返回值。
我们把ToUpper()函数单独放在assifunc.cpp里。
同样,下面的代码加在该文件中原有的代码之后。
不过本文件不需要include,因为没有用到cin,cout等。
//小写字母转换为大写
//参数:
c待转换的字符
//返回值:
转换后的字符,如果原字符不是小写字母,则为原字符
charToUpper(charc)
{
intca='A'-'a';//大写字母和小写字母之间差距多少?
if(c>='a'&&c<='z')
c+=ca;
returnc;
}
至此,所有自定义函数都已完成定义(实现),而三个文件的主要内容也以确定。
让我们看看示意图:
main.cpp中的main()函数调用了三个函数。
回忆我们学习过的“如何调用函数”的知识,当前代码在调用一个函数时,必须能“看到”这个函数。
尽管CalcTotal()、CalcAverage()、ToUpper()三个函数所在文件都在同一工程里,但是在main.cpp里的代码,还是看不到它们。
想一想我们以前说的“请修理工”的比喻。
现在情况是:
在你所住的小区,甚至就是同一楼道里,就有一个电视修理工,但可惜你们互不认识,所以当你电视坏了,想“调用”一个修理工时,你还是找不到修理工。
哎!
要是有它的名片就好了。
让我们试试看,按Ctrl+F9,编辑该工程。
出错!
正好是三个错。
分别告诉我们调用了三个没有定义的函数(Calltoundefinedfunction...)。
(如果你出现的是一堆错,那有可能是你没有在前两个文件内最首行写:
“#include”
或者是你有些代码输入有误。
)
如何消除这三个错?
两种方法。
第一种方法就是以前我们在讲“如何调用函数”的时候所说的,直接在调用直接声明要调用的函数。
这里写出代码,算做是一次复习,然后我们将讲该方法不好之处。
在main.cpp的main()函数之前加入如下三行函数声明:
voidCalcTotal(intn);
voidCalcAverage(intn);
charToUpper(charc);
intmain(intargc,char*argv[])
{
......
}
(上例中,尽管你可以将三行函数声明写在main()函数体内,但不建议这样做)。
如果你一切输入正确的话,现在按Ctrl+F9或F9将可以完成编译或运行。
对于现在这个工程,这种方法确实也不能指责它有何不利之处。
问题在于,如果我们还有其它文件中代码需要调用到这三个函数,我们就不得不在其它文件中也一一写上这三行声明。
所以另一种方法是:
把源文件中需要对外“共享”的函数声明统一写到某个头文件,然后凡是需要用到的其它文件,直接使用“#include"语句来包含该头文件,从而获得这些函数声明。
14.3如何写头文件
在CB中,如果你通过上小节的方法新建个单元文件,那么CB将自动同时生成源文件和头文件。
其实在CB里,源文件和头文件合称为单元文件,它们有相同的文件名,而扩展名一者为.cpp,另一为.h。
14.3.1在头文件内加入函数声明
头文件:
mainfunc.h
CalcTotal()和CalcAverage()函数定义在mainfunc.cpp文件里,所以它们的声明最好写在对应的头文件mainfunc.h内。
下面我们就来看如何在头文件mainfunc.h内增加函数声明。
一开始,头文件内有以下这些代码。
另外,我增加了一行用于标明我们新加的代码应写在哪里。
//---------------------------------------------------------------------------
#ifndefmainfuncH
#definemainfuncH
//---------------------------------------------------------------------------
/*!
!
!
头文件中,我们新增的代码必须写在此处!
!
!
*/
#endif
和源文件中新增代码添加在最后不一样,头文件中新加代码必须在#endif之前插入。
所以本例中,加完函数声明的代码应如其下所示。
(另外,CB总是在头文件的第二行留了一行空白行,我不知道它这是有意还是无意。
总之这里是我们写本文件总体注释的好地方。
记住,头文件像名片,用于让别人看,很有必要写得详细点)
//---------------------------------------------------------------------------
//主要操作函数
#ifndefmainfuncH
#definemainfuncH
//---------------------------------------------------------------------------
//计算总和:
voidCalcTotal(intn);
//计算平均值:
voidCalcAverage(intn);
//---------------------------------------------------------------------------
#endif
这就是“在头文件中声明函数”的整个过程。
下面是另外一个头文件。
头文件:
mainfunc.h
//---------------------------------------------------------------------------
//辅助操作函数
#ifndefassifuncH
#defineassifuncH
//---------------------------------------------------------------------------
//将字符转换成大写
charToUpper(charc);
#endif
今天我们学的是如何在头文件内声明函数,以后我们需要在头文件内声明变量,或者定义新的数据类型,它们都一样需要在上述的#endif之前加入。
14.3.2最常见的预编译语句
现在来解释这三行话:
#ifndefmainfuncH
#definemainfuncH
#endif
中间的#definemainfuncH我们有点脸熟。
在第五章《变量与常量》中,我们讲过用宏表示常数。
语法为:
#define宏名称宏值
比如,定义一个∏值:
#definePAI3.14159
这里我们学的是宏定义的另一种用法:
仅仅定义一个宏,不需要给出它的值,语法为:
#define宏名称
比如:
#definemainfuncH
定义了一个宏:
mainfuncH。
如果你无法理解“宏”这个词,不妨就当把它解释成“记号”。
即编译器通过该语句,做了一个记号,记号名称为:
mainfucH。
这么做的作用是什么呢?
我们继续看上下文。
#ifndef中,if是“如果”,n是no,即“还没有”,def是define,即“定义”,那么:
#ifndefmainfuncH意为:
“如果还没有定义mainfuncH这个宏”,那么……
那么之后做什么呢?
就是一直到#endif之间的语句。
总的再来看一遍:
mainfunc.h中的主要内容:
#ifndefmainfuncH
#definemainfuncH
voidCalcTotal(intn);
voidCalcAverage(intn);
#endif
当编译第一次编译mainfunc.h文件时,宏mainfuncH还没有定义,因些,编译器通过对#definemainfuncH的编译而产生了宏mainfuncH。
当编译器第二次编译到mainfunc.h文件时,宏mainfuncH已经存在,所以该头文件被直接跳过,不会重复处理该头文件中内容,比如上面的两个函数声明。
你可能会问两个问题:
第一,为什么编译器可能多次编译到同一个头文件?
第二,为什么源文件,比如mainfunc.cpp就不需要用到#ifndef...#endif?
这两个问题只要回答了其中一个,另一个也就自然消解。
这是由头文件特性所决定的。
头文件是用来被别人包含(include)的。
谁都可以指定要包含某一头文件,这样就可能造成对该头文件的重复包含。
假设有头文件head.h。
如果A文件包含了head.h,而B文件也包含了head.h,那么编译器不会在编译A和编译B时,都要对该头文件尝试编译一次。
另外,头文件本身也可以包含另一个头文件,这种情况下,各文件之间互相嵌套包含的情况就更多了。
源文件(.c或.cpp)尽管可以,但一般不被用来被别的文件包含,所以不需要在源文件中加这些语句。
当然,如果需要,你也可以源文件中使用#ifndef...#endif。
每生成一个头文件,包括在重命名它时,CB会为我们取好该头文件中,上述的宏名称,它取该头文件的全小写文件名,加上一个大写的‘H’字母,比如:
"mainfuncH"。
请大家不要改变该宏的名称,以免出错。
除了#ifndef...#endif语句外,还有它的相反逻辑的语句:
#ifdef...#endif它是在如果有定义某个宏,那么,编译将继续其后的语句。
另外就像有if语句,还有if...else...语句一样,有#ifdef...#endif,也就还有这个语句:
#ifdef
......
#else
......
#endif
不过这些都和我们这里的头文件相关不大,我们暂时不讲。
最后我们来解释一个名词“预编译”。
编译器在编译代码时,至少需要两遍的编译处理,其中第一次,就是专门用于处理所有以#开头的语句,如上述的#ifndef...#endif、#define等等。
这一遍处理,我们称为预编译。
14.4如何使用头文件
事实上我们经常在使用头文件。
不过,以前我们一直在使用别人的头文件,今天是第一次使用我们自已的写的头件。
以前,我们几乎每个例子,包括今天的例子中,都需要在源文件的顶部写上一行:
#include
或者:
#include
iostream.h和stdio.h都是CB提供给我们的头文件。
这些头文件随CB安装时,被保存在固定的文件夹内。
今天的例子中,main.cpp需要使用到在mainfunc.h和assifunc.h。
这是我们自己写的头文件,它们保存在我们自定的文件夹中。
包含自已写的头文件,和包含