第14章 程序的文件结构.docx

上传人:b****4 文档编号:12057550 上传时间:2023-04-16 格式:DOCX 页数:30 大小:123.72KB
下载 相关 举报
第14章 程序的文件结构.docx_第1页
第1页 / 共30页
第14章 程序的文件结构.docx_第2页
第2页 / 共30页
第14章 程序的文件结构.docx_第3页
第3页 / 共30页
第14章 程序的文件结构.docx_第4页
第4页 / 共30页
第14章 程序的文件结构.docx_第5页
第5页 / 共30页
点击查看更多>>
下载资源
资源描述

第14章 程序的文件结构.docx

《第14章 程序的文件结构.docx》由会员分享,可在线阅读,更多相关《第14章 程序的文件结构.docx(30页珍藏版)》请在冰豆网上搜索。

第14章 程序的文件结构.docx

第14章程序的文件结构

第十四章 程序的文件结构

14.1源文件和头文件

14.2如何创建多个单元文件

14.3如何写头文件

  14.3.1在头文件内加入函数声明

  14.3.2最常见的预编译语句

14.4如何使用头文件

14.5变量在多个源文件之间的使用

  14.5.1变量声明

  14.5.2多个文件中共享变量的实例

14.6附:

如何单独生成一个头文件

 

程序是由什么组成的?

学习到今天,我们至少有两个答案:

第1,程序由代码语句组成。

正是一行行的代码,组成了一个完整的程序。

第2,程序由函数组成。

一个个函数之间的互相调用,最终构建出一个完整的程序。

今天我们又有一个新的回答:

“程序由文件组成”。

程序为什么需要使用多个文件?

一个小的程序,可以只写一个源文件,但程序稍微一大,就需要将其中不同的逻辑实现放到不同的源文件.对于需要多人一起开发的软件,自然更需要多个源文件。

 

14.1源文件和头文件

 

和别的一些语言不同,C,C++的代码文件有“头文件”和“代码文件”之分。

二者合起来我们称为单元(Unit)文件。

 

扩展名为.c或.cpp的文件,主要用以实现程序的各种功能,我们称为代码文件。

扩展名为.h的文件,称为头文件。

在头文件里主要写一些函数、数据(包括数据类型的定义)、等的声明,这样可以在多个.c或.cpp文件内共享这些函数、数据。

第12章我们提过到头文件的功能。

说它可以起到函数“名片夹”的作用。

 

大都数时候,源文件和头文件是对应出现的,比如有一个A.cpp的源文件,就会有一个A.h的头文件。

这种情况在我们写应用程序时,更是常见。

所以C++Builder对此进行了强化。

比如,它支持在同名源文件和头文件之间通过热键来回切换。

在CB6.0里,编辑器打开对应的源文件和头文件时,将显示为同一页下的两个子页。

 

我们来实际动手看看源文件与头文件在CB里的对应关系。

 

运行C++Builder6或5。

这一次我们需要一个空白的Windows工程。

很有可能,在你打开CB时,它就自动为你打开了一个工程。

为了不出错,我们还是亲自建一个。

CB6请使用主菜单:

File|New|Application;而CB5则使用:

File|NewApplication新建一个Windows空白工程。

如果在这过程中CB出现是否存盘的询问,请回答不存盘。

 

找到“代码窗口”。

如果你看到的是一个叫"Form1"的表单,请按F12,“代码窗口”将跑到前面。

它的标题应该是默认的"Unit1.cpp"。

正是当前代码文件的文件名。

如下图:

 

 

对于CB6,还可以看到在该窗口的底部有这样一个分页:

 

 

源文件:

Unit1.cpp和头文件:

Unit1.h并列着,我们可以方便地选择。

至于"Diagram",称为“图解”。

这是一个给这个源文件加配套注解,及表单上各控件的依赖关系的地方。

如果是一个开发小组在进行共同开发,严格地要求每个成员为每个单元文件写上“Diagram”,可以更好地实现程序员之间的沟通。

CB5没有这些,不过下面的热键操作两个版本均一样的,要求大家记住。

按Ctrl+F6可以在源文件和头文件之间来回切换。

请大家试试。

这个简单的操作将在我们今后的编程过程中高频率地使用。

 

14.2如何创建多个单元文件

 

前面我们在“Windows应用程序工程”中看到了头文件与源文件的匹配关系,在“控制台”的工程中,也同样存在。

不过由于控制台经常只用来写一些小小的程序,所以往往只需一个源文件即可。

由于只有一个源文件,所以也就不存在函数、数据在多个文件之间“共享”的需要,因此边头文件也就可以不提供。

 

那么,是不是只有在程序很大,或者只有在有很多人同时开发一个软件时,才需要多个源文件呢?

这就好像你家里只有两本书:

《红楼梦》和《格林童话》,是把它们放在同一个抽屉里呢?

还是分开放到两个抽屉里?

我觉得后者是比较好的选择。

因为我们常常希望家里看《格林童话》的人,最好不要去看《红楼梦》。

程序也一样,最好把不同的逻辑实现,放到不同的源文件中。

 

下面我们做一个实例。

例子的代码我们都已经学过。

目标是实现一个可以求统计值和平均值的程序。

根据我们现在所学的情况,我把这个工程中的代码分为三个源代码:

其一:

主程序。

就是main()函数所在的代码。

这个源文件实现总的流程。

我将该文件取为main.cpp。

其二:

计算总和及计算平均值的代码。

这个源文件负责用户计算过程,也包括每个过程所需输入输出。

该文件将被存盘为mainfunc.cpp。

意为主要功能。

其三:

assifunc.cpp。

表示辅助功能函数所在代码。

它只提供一个函数:

将用户输入的大写或小写的字母'Y'或'N'确保转换为大写。

这个函数将main()主函数内,判断用户是否继续时用到。

 

新CB新建一个控制台程序(如果你还开着上个程序,先选File|CloseAll关闭它)。

CB会自动生成第一个文件,不过现在的名字为“Unit1.cpp"。

接下来是一项新工作,我们来添加两人新的单元文件,即上面说的“其二”和“其三”。

CB6:

File|New|Unit;CB5:

File|NewUnit。

请进行两次以上操作,CB将为我们生成新的两个单元文件:

Unit2.cpp和Unit3.cpp。

大家可以再试试Ctrl+F6。

(注意,第一个单元文件:

Unit1.cpp没有配套的.h文件,所以不要在该文件里尝试Ctrl+F6)。

然后选择File|SaveAll。

全部存盘,最好不要存在CB默认的目录下。

记得按以下关系重命名:

Unit1.cpp存盘为main.cpp;

Unit2.cpp存盘为mainfunc.cpp;

Unit3.cpp存盘为assifunc.cpp;

至于总的工程,随你便,我图方便,还是叫:

Project1.bpr。

 

(现在我们第一次在一个工程中用到多个源文件。

所以你得学会如何快速打开一个工程中某个源文件——当然,现在这三个文件都已经打开着,不过假设你有点事关闭CB,我们不希望下回打开这个工程时,你“找”不到第2和第3个文件了——请点击CB工具栏上的这个图标:

,将出现源文件列表对话框,如左图)

 

 

接下来讲在这三个文件中,我们分别写些什么?

大多数代码我们都已经在前面学过,所以我对代码的功能不作太多的解释。

我们的重点是:

三个源文件之间的代码如何实现“沟通”。

 

第一个文件:

main.cpp用来实现程序的主流程。

在main.cpp中的main()函数内,我们加入代码。

 

#include

......

intmain(intargc,char*argv[])

{

  charch;

  intcount;//求总和或平均值时,需要输入的成绩个数

 

  do

  {

     cout<<"1)求总和"<

     cout<<"2)求平均"<

    

     cout<<"请选择(1或2)";

     cin>>ch;

     

     //输入有误,重输:

     if(ch!

='1'&&ch!

='2')

     {

        cout<<"输入有误,请重新输入!

"<

        continue;

     }

     

     cout<<"请输入个数:

";

     cin>>count;

     

     //根据用户的选择,调用不同函数:

     switch(ch)

     {

        case'1':

           CalcTotal(count);//需要调用的函数之一

           break;

        case'2':

           CalcAverage(count);//需要调用的函数之一

           break;

     }

        

     //是否继续:

     cout<<"是否继续?

(y/n)";

     cin>>ch;

     

     //确保转换为大写:

     ch=ToUpper(ch);//需要调用的函数之一

  }

  while(ch=='Y');

  

  return0;

}

 

代码中,红色部分的注释表明,主函数main()需要调用到三个自定义函数。

但现在我们一个也没有定义。

和往常把所有的函数定义在同一个代码文件中不同,今天我们需要把它们分开到不同的代码文件。

 

第二个文件:

mainfunc.cpp存放和计算有关的两个过程(函数)。

先看:

CalcTotal()和CalcAverage()。

这两个函数我们将在mainfunc.cpp文件内定义。

你可能又忘了“定义”这个术语?

呵,就是“实现”,更白点,就是在mainfunc.cpp文件内“写”这两个函数。

 

下面是mainfunc.cpp的内容。

在我们输入以下代码时,mainfunc.cpp已经有了一些必要的内容,下面的代码,除了“#include..”一行在文件最首外,其它均在原有内容之后添加。

 

#include//在文件最首行

......

//---------------------------------------------------------------------------

//求总和的过程

//参数:

n用户需要输入的个数

voidCalcTotal(intn)

{

  intnum;

  intsum=0;

  

  for(inti=0;i

  {

     cout<<"请输入第"<

";

     cin>>num;

     

     sum+=num;

  }

  

  cout<<"总和为:

"<

}

//---------------------------------------------------------------------------

//求平均值的过程

//参数:

n用户需要输入的个数

voidCalcAverage(intn)

{

  intnum;

  intsum=0;

  floatave;

  for(inti=0;i

  {

     cout<<"请输入第"<

";

     cin>>num;

  

     sum+=num;

  }

  //注意不要除0出错:

  if(n>=0)

  {

     ave=(float)sum/n;

     cout<<"平均值:

"<

  }

  else

  {

     cout<<"个数为0,不能求平均。

"<

  }

}

//---------------------------------------------------------------------------

 

第三个文件:

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。

这是我们自己写的头文件,它们保存在我们自定的文件夹中。

包含自已写的头文件,和包含

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1