• C++的程序的文件结构(zt)


          因项目需要,需要对C++的程序的文件结构有所掌握:因此在网上找了下面的文章供学习和参考。希望对刚入门的C++朋友能提供一些帮助。
    转载:http://www.d2school.com/

    程序是由什么组成的? 学习到今天,我们至少有两个答案:

    第1,程序由代码语句组成。正是一行行的代码,组成了一个完整的程序。

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

    今天我们又有一个新的回答:“程序由文件组成”。

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

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

     

    14.1 源文件和头文件

     

    和别的一些语言不同,C,C++的代码文件有“头文件”和“代码文件”之分。二者合起来我们称为单元(Unit)文件。

     

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

    扩展名为 .h 的文件,称为头文件。在头文件里主要写一些函数、数据(包括数据类型的定义)、等的声明,这样可以在多个.c或.cpp文件内共享这些函数、数据。第12章我们提过到头文件的功能。说它可以起到函数“名片夹”的作用。 

    大都数时候,源文件和头文件是对应出现的,比如有一个 A.cpp 的源文件,就会有一个 A.h 的头文件。这种情况在我们写应用程序时,更是常见。所以C++ Builder对此进行了强化。比如,它支持在同名源文件和头文件之间通过热键来回切换。在CB6.0里,编辑器打开对应的源文件和头文件时,将显示为同一页下的两个子页。

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

    运行 C++ Builder 6或5。

    这一次我们需要一个空白的Windows工程。很有可能,在你打开CB时,它就自动为你打开了一个工程。为了不出错,我们还是亲自建一个。CB6请使用主菜单:File | New | Application;而CB5则使用:File | New Application 新建一个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 | Close All关闭它)。CB会自动生成第一个文件,不过现在的名字为“Unit1.cpp"

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

    CB6 :File | New | Unit;CB5:File | New Unit

    请进行两次以上操作,CB将为我们生成新的两个单元文件:Unit2.cppUnit3.cpp。大家可以再试试 Ctrl + F6(注意,第一个单元文件:Unit1.cpp 没有配套的.h文件,所以不要在该文件里尝试Ctrl + F6)

    然后选择File | Save All。全部存盘,最好不要存在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 <iostream.h>

    ... ...

    int main(int argc, char* argv[])

    {

       char ch;

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

     

       do

       {

          cout << "1)求总和" << endl;

          cout << "2)求平均" << endl;

        

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

          cin >> ch;

         

          //输入有误,重输:

          if(ch != '1' && ch != '2')

          {

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

             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');

      

       return 0;

    }

     

    代码中,红色部分的注释表明,主函数main()需要调用到三个自定义函数。但现在我们一个也没有定义。和往常把所有的函数定义在同一个代码文件中不同,今天我们需要把它们分开到不同的代码文件。

     

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

    先看:CalcTotal()和CalcAverage()。这两个函数我们将在mainfunc.cpp文件内定义。你可能又忘了“定义”这个术语?呵,就是“实现”,更白点,就是在mainfunc.cpp文件内“写”这两个函数。

     

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

     

    #include <iostream.h> //在文件最首行

    ... ...

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

    //求总和的过程

    //参数:n 用户需要输入的个数

    void CalcTotal(int n)

    {

       int num;

       int sum = 0;

      

       for(int i=0;i<n;i++)

       {

          cout << "请输入第" << i+1 <<"个整数:";

          cin >> num;

         

          sum += num;

       }

      

       cout << "总和为:" << sum << endl;

    }

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

    //求平均值的过程

    //参数:n 用户需要输入的个数

    void CalcAverage(int n)

    {

       int num;

       int sum = 0;

       float ave;

       for(int i=0;i<n;i++)

       {

          cout << "请输入第" << i+1 <<"个整数:";

          cin >> num;

      

          sum += num;

       }

       //注意不要除0出错:

       if( n >=0 )

       {

          ave = (float)sum / n;

          cout << "平均值:" << ave << endl;

       }

       else

       {

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

       }

    }

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

     

    第三个文件:assifunc.cpp 用以存放辅助作用的函数,现在只有一个。

    现在还差一个函数:ToUpper()。这个函数用来将用户输入的某个小写字母转换为大写。当然,如果用户输入的不是小写字母,那就不用转换。和上面的两个函数不同,它需要返回值。

    我们把ToUpper()函数单独放在assifunc.cpp里。同样,下面的代码加在该文件中原有的代码之后。不过本文件不需要include <iostream.h> ,因为没有用到 cin,cout等。

     

    //小写字母转换为大写

    //参数: c 待转换的字符

    //返回值: 转换后的字符,如果原字符不是小写字母,则为原字符

    char ToUpper(char c)

    {

       int ca = 'A' - 'a'; //大写字母和小写字母之间差距多少?

       if(c >= 'a' && c <= 'z')

          c += ca;
     

       return c;

    }

     

    至此,所有自定义函数都已完成定义(实现),而三个文件的主要内容也以确定。让我们看看示意图:

     

     

    main.cpp中的main()函数调用了三个函数。回忆我们学习过的“如何调用函数”的知识,当前代码在调用一个函数时,必须能“看到”这个函数。尽管CalcTotal()、CalcAverage()、ToUpper()三个函数所在文件都在同一工程里,但是在main.cpp里的代码,还是看不到它们。想一想我们以前说的“请修理工”的比喻。现在情况是:在你所住的小区,甚至就是同一楼道里,就有一个电视修理工,但可惜你们互不认识,所以当你电视坏了,想“调用”一个修理工时,你还是找不到修理工。哎!要是有它的名片就好了。

    让我们试试看,按Ctrl + F9,编辑该工程。出错!

    正好是三个错。分别告诉我们调用了三个没有定义的函数(Call to undefined function ...)。

     

    (如果你出现的是一堆错,那有可能是你没有在前两个文件内最首行写:

    “#include <iostream.h>”

    或者是你有些代码输入有误。)

     

    如何消除这三个错?两种方法。

    第一种方法就是以前我们在讲“如何调用函数”的时候所说的,直接在调用直接声明要调用的函数。这里写出代码,算做是一次复习,然后我们将讲该方法不好之处。

     

    在 main.cpp 的 main()函数之前加入如下三行函数声明:

     

    void CalcTotal(int n);

    void CalcAverage(int n);

    char ToUpper(char c);

     

    int main(int argc, 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 内增加函数声明。

    一开始,头文件内有以下这些代码。另外,我增加了一行用于标明我们新加的代码应写在哪里。

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

    #ifndef mainfuncH

    #define mainfuncH

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

    /* !!!头文件中,我们新增的代码必须写在此处!!!  */

    #endif

     

    和源文件中新增代码添加在最后不一样,头文件中新加代码 必须在#endif之前插入。所以本例中,加完函数声明的代码应如其下所示。(另外,CB总是在头文件的第二行留了一行空白行,我不知道它这是有意还是无意。总之这里是我们写本文件总体注释的好地方。记住,头文件像名片,用于让别人看,很有必要写得详细点)

    //---------------------------------------------------------------------------
       //主要操作函数

    #ifndef mainfuncH

    #define mainfuncH

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

    //计算总和:

    void CalcTotal(int n);

    //计算平均值:

    void CalcAverage(int n);

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

    #endif

     

    这就是“在头文件中声明函数”的整个过程。下面是另外一个头文件。

     

    头文件:mainfunc.h

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

    //辅助操作函数

    #ifndef assifuncH

    #define assifuncH

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

    //将字符转换成大写

    char ToUpper(char c);

    #endif

     

    今天我们学的是如何在头文件内声明函数,以后我们需要在头文件内声明变量,或者定义新的数据类型,它们都一样需要在上述的#endif之前加入。

     

    14.3.2 最常见的预编译语句

     

    现在来解释这三行话:

    #ifndef mainfuncH

    #define mainfuncH

     

    #endif

     

    中间的 #define mainfuncH 我们有点脸熟。在第五章《变量与常量》中,我们讲过用宏表示常数。语法为:

    #define 宏名称 宏值

     

    比如,定义一个∏值:

    #define PAI 3.14159

     

    这里我们学的是宏定义的另一种用法:仅仅定义一个宏,不需要给出它的值,语法为:

     

    #define 宏名称

     

    比如:#define mainfuncH

     

    定义了一个宏:mainfuncH。如果你无法理解“宏”这个词,不妨就当把它解释成“记号”。即编译器通过该语句,做了一个记号,记号名称为:mainfucH。

    这么做的作用是什么呢?我们继续看上下文。

     

    #ifndef 中, if 是“如果”,n 是 no,即“还没有”,def是 define,即“定义”,那么:

    #ifndef mainfuncH 意为:“如果还没有定义mainfuncH这个宏”,那么……

    那么之后做什么呢?就是一直到 #endif之间的语句。

     

    总的再来看一遍:

     

    mainfunc.h 中的主要内容:

     

    #ifndef mainfuncH

    #define mainfuncH

     

    void CalcTotal(int n);

    void CalcAverage(int n);

     

    #endif

     

    当编译第一次编译mainfunc.h文件时,宏 mainfuncH 还没有定义,因些,编译器通过对 #define mainfuncH的编译而产生了宏 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 <iostream.h>

    或者:

    #include <stdio.h>

     

    iostream.h和stdio.h都是CB提供给我们的头文件。这些头文件随CB安装时,被保存在固定的文件夹内

    今天的例子中,main.cpp 需要使用到在 mainfunc.h 和 assifunc.h。这是我们自己写的头文件,它们保存在我们自定的文件夹中

    包含自已写的头文件,和包含CB提供的头文件并无多大区别。

    请在 main.cpp 代码顶部,加入以下黑体部分:

     

    #include <iostream.h>

    #include "mainfunc.h"

    #include "assifunc.h"

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

     

    二者的细小区别是,包含CB提供的头文件时,用尖括号<>;而包含我们自已的头文件时,使用双引号“”。CB据此判断如何找到指定的头文件。<>相当于告诉CB,这是你自已提供的头文件,到你安装时的头文件目录下找去吧,而“”则是告诉CB,是这我自已写的头文件,请首先到我当前工程所在目录下查找,如果找不到,再到别的可能的头文件目录下找这个文件。(别的还有什么目录可能存放当前工程的头文件呢?稍后会讲。)

     

    现在,我们让main.cpp包含了它想要的头文件,头文件内有它所需函数的正确声明,那么main.cpp中原来的这三行就多余了:

    void CalcTotal(int n);

    void CalcAverage(int n);

    char ToUpper(char c);

    请删除。 然后,按F9,程序正确编译,然后运行。这里我们不关心它的运行结果。

     

    现在来看一眼在CB中如何设定某一工程的头文件目录。

    必须先说清楚,在相当长的一段时间内,我们并不需要去进行此设置。对于CB提供的头文件,它们固定就在CB安装时自动存储的某些目录下,你只要记得包含这些头文件时,使用<>即可。对于我们自已写的头文件,我们都把它们和工程文件存放在同一目录下,暂时还没有什么理由需要把某个或某些头文件“扔”在别的目录下。所以,记住在包含自己的头文件时,对使用“”即可。

    首先保证当前CB正打开着上面的那个例子工程。

    然后,主菜单: Project | Options 或按 Ctrl + Shift + F11,打开“工程设置(Project Options)”对话框,并切换到“目录与条件(Directories/Conditionals)”页:

     

    图中有关目录的设置共六行,我们说其中常用的四行。

    最主要的,当然是今天所说的“头文件目录”。当 CB 编译时,当它遇到这样一行:

     

    #include "xxxx.h"

    那么,它必须找到文件xxxx.h。如果,你写的是绝对路径:#include "c:\abc\123\xxxx.h",那自然没有查找这一说,不过我们不会喜欢这样写程序,因为我们不希望源代换个位置就得一一去改那些绝对路径。事实上我们不可能把头文件到处放,总是固定那么几个目录,绝大多数就一个:所有源文件和头文件都在当前工程所在目录下。这里可以添加,删除,修改一些目录位置,CB将按本设置中的目录次序去查找头文件。

    请点击“头文件目录”右边,带 "..."的小按钮。出来一个新的对话框:

    ($BCB) 表示Borland C++Builder 的安装目录。

     

    在这里,你可以修改(Replace),增加(Add),删除(Delete),调整次序(向上和向下的蓝箭头)各个头文件目录。CB6还提供了对无效目录的判断,如果列表中列出的某个目录实际上并不存在对应的文件夹,则将以灰色显示,并且可以用"Delete Invalid Paths"按钮全部删除。

     

    我们什么也不用做。点 Cancel, 放弃就是。

     

    其它目录的设定,操作完全一样。

     

    关于在工程中如何使用头文件,我们就说这些了。

     

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

    前面讲的是,通过在头文件中声明函数,可以达到让这个函数被其它文件共用的作用。同样地,变量也可以在多个源文件之间“共享”。下面我们就要讲,如何通过声明变量,以达到让其它文件共用同一个变量的目的。

     

    14.5.1 变量声明

    先说说“声明变量”。好像以前的课程只教过我们:定义变量,定义函数,声明函数,没有讲过“声明变量”啊?

     

    我们很早就学过如何定义一个变量。(5.1.2

     

    比如:

     

    //定义一个整型变量:

    int age;

     

    //然后,在后面的某处代码中使用这个变量:

    ... ...

    age = 18;

    cout << age << endl;

    ... ...

     

     

    但是,我们没有遇到过如何声明一个变量。这是因为,定义一个变量的同时,也就声明了一个变量;绝大多数的时候,我们都是可以需要某个变量时,直接定义它。

     

    今天的情况有点不一样。我们需要在某个源文件中定义一个变量,然后,在另外一个源文件中使用这个变量。

     

    仍以前面 age 变量为例:

     

    //我们在 A.cpp 文件中定义了这个变量:

    int age;

     

    //然后,在 B.cpp 文件中要使用这个变量:

    age = 18;

    cout << age << endl;

     

    问题就出来了:在编译 B.cpp 文件时,编译器会说:“age 这个变量没有定义啊?”——当编译器在编译 B.cpp时,它并不懂得去A.cpp里去找有关 age 的定义。

    那么,能不能在B.cpp里再定义一次age变量呢?

     

    //A.cpp文件中:

    int age;

     

    //B.cpp文件中:

    int age;

    age = 18;

    cout << age << endl;

     

    这样,单独编译A.cpp,或B.cpp,都可以通过。但一旦要编译整个工程,编译器又会报错:“怎么有两个 age 变量的定义啊”?

    不要嘲笑编译器为什么这么笨笨。C,C++是一门严谨的的计算机语言,我们不能指望编译器会“智能”地猜测程序员的企图。

     

    解决方法是,仅在一处定义变量,别的代码需要用到该变量,但无法看到前面的定义时,则改为“声明变量”。

     

    声明变量的语法:

    extern 数据类型 变量名

     

    和定义变量的语法相比,多了前面的 extern 这个关键字。

     

    extern 意为“外来的”···它的作用在于告诉编译器:有这个变量,它可能不存在当前的文件中,但它肯定要存在于工程中的某一个源文件中。

     

    这就好像:微软公司在北京招人,微软的报名方法是:在北京的应聘者必须当天去面试,而外地应聘者则通过发e-mail先报名,然后以后再去面试。 在C,C++里,不处于当前源文件中的变量被称为外部变量。比喻中,发e-mail就相当于外部变量在某一个源中写个声明。声明什么呢?就是声明“我存在啊!虽然我现在不在这里,但是我真的存在!”

     

    上例中,正确的代码应该这样写:

     

    //A.cpp文件中:

    int age;

     

    //B.cpp文件中:

    extern int age;

    age = 18;

    cout << age << endl;

     

    变量 age 是在A.cpp文件里定义的,当B.cpp文件要使用它时,必须先声明。这就是我们讲半天课的核心。

     

    (有些教材并不认为 extern int age; 是在声明一个变量,它们把这也称为是“定义变量”的一种,只不过它是定义了一个名部变量。我认为这样认为不好,一来它造成了学习者认为“变量可以重复定义”的错误认为,二来它也造成了不统一,函数有“定义”和“声明”两种形式,而变量都没有“声明”。

    可能你会说,现在也不统一啊?函数声明没有“extern",而变量却需要?呵呵,其实恰恰相反。函数声明本来也是需要一个“extern”的,比如:

     

    extern void CalcTotal(int n);

    你在代码里这样完全正确!只不过由于函数声明和函数定义的格式区别很大,(声明没有函数体,定义则必须有函数体),所以这个extern就算不写,也可以让编译器认出来它是一个“声明”。结果就规定可以不写"extern"了。

    而变量呢?

    extern int age;     //这是声明

    int age;            //这是定义

    你看看,不写"extern"可不行! 就靠它来区分是定义还是声明了。

    如此而已。)

     

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

     

    做一个最简单的例子。新建一个控制台工程。然后再加一个单元文件。把工程存盘为Project1.bpr,把两个源文件分别存盘为Unit1.cpp、Unit2.cpp (即,都采用默认文件名)。

     

    程序内容是:在 Unit1.cpp 内定义一个变量,即:int age,并且,要求用户输入。在Unit2.cpp里,写一函数,OutputAgeText(),它根据 age 的值, 输出一些文本。

     

    请问,变量 age 在哪里定义?又在哪里声明?

     

    定义指定是在 Unit1.cpp 文件里,而声明,则可以在 Unit2.cpp内直接声明(如上例中的红色代码),也可以是在头文件 Unit1.h 里声明,然后在 Unit2.cpp 内使用 include 来包含 Unit1.h。 事实让,声明也可以放在 Unit2.h内。只要能让Unit2.cpp“看到”这个声明即可。这一点和函数的声明一个道理。

     

    我们采用放在Unit2.cpp中的方法,该方法所需代码如下:

     

    //Unit1.cpp 内的主要代码:

     

    #include <iostream.h>

    #include <conio.h>

    #pragma hdrstop

    #include "Unit2.h"

    ... ...

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

    int age; //全局变量,年龄

    #pragma argsused

    int main(int argc, char* argv[])

    {

       cout << "请输入您的年龄:" ;

       cin >> age;

     

       //调用Unit2.cpp中的函数,该函数根据age,作出相应输出

       OutAgeText();  

      

       getch();

       return 0;

    }

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

     

     

    //Unit2.cpp 中的主要代码:

    #include <iostream.h>

    ... ...

    extern int age;  //需要Unit1.cpp内定义的变量

     

    //报名参加“没有弯路”的学员各行业,年龄段也各处不同,在此,我们用这个函数作为共勉!

    void OutAgeText()

    {

       if(age < 15)

          cout  << "计算机要从娃娃抓起!" << endl;

       else if(age < 25)

          cout << "青春年华,正是学习编程的黄金时代!" << endl;

       else if(age < 35)

          cout << "学习编程需要热情,更需要理性!我和您一样,也在这个年龄段!"<< endl;

       else if(age < 45)

          cout << "活到老,学到老!何况您还未老。杀毒王王江民,不也在这个时候才开始学习电脑吗?" << endl;

       else

          cout <<  "前辈,只要您像学书法一样潜心学编程!您一定会有收获!" << endl;

    }

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

     

    //Unit2.h 的主要代码:

     

    //声明OutAgeText()函数,供Unit1.cpp使用

    void OutAgeText();

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

    请大家完成这个工程,直到能正确运行。

     

    现在我们得到一个印象:当我们定义了一个函数或变量之后,似乎所有的源代码文件中都可以使用它,只要你在使用之前写一下相应的声明。

    这样会不会带来麻烦了?想象一下,你在A文件定义了一个变量: int i, 那么以后你在别的文件里就不能再定义这个变量了!原因前面已经说过,编译器(或链接器)会说有两个变量重名。函数也一样,尽管它有重载机制,便那也只能是有限制地允许函数重名。

     

    事实上,上例中的 int age 是一个全局变量。关于“全局”的解释,需要引起C,C++程序的另一话题:作用范围。这是下一章的内容。在那一章里,我们将看到,大部分变量只在它一定的作用范围内“生存”,不同的作用范围的变量就可以毫无障碍地重名了。

    休息休息(该点眼药水了···),然后学习本章附加一节。


     

  • 相关阅读:
    1877. 数组中最大数对和的最小值 力扣(中等) 简单题,sort+贪心
    1838. 最高频元素的频数 力扣(中等) 尺取法 是我做不出来的题
    面试题 08.09. 括号 力扣(中等) 是我想不出来的回溯
    js删除数组里的某个元素
    Yiwen with Sqc 题解(dp)
    Link with EQ 题解(dp)
    Robots 题解(暴力bitset)
    P3232 [HNOI2013]游走 题解(图上随机游走问题 高斯消元
    怀旧游戏 题解(逆向bfs打表博弈)
    F. The Number of Subpermutations 题解(异或hash维护全排列)
  • 原文地址:https://www.cnblogs.com/Charles2008/p/1559935.html
Copyright © 2020-2023  润新知