•  C++语言的创建初衷是“a better C”,但是这并不意味着C++中类似C语言的全局变量和函数所采用的编译和连接方式与C语言完全相同。作为一种欲与C兼容的语言, C++保留了一部分过程式语言的特点(被世人称为“不...
    
    
    1.引言
      C++语言的创建初衷是“a better C”,但是这并不意味着C++中类似C语言的全局变量和函数所采用的编译和连接方式与C语言完全相同。作为一种欲与C兼容的语言,
    C++保留了一部分过程式语言的特点(被世人称为“不彻底地面向对象”),因而它可以定义不属于任何类的全局变量和函数。但是,C++毕竟是一种面向对象的程序设计语言
    ,为了支持函数的重载,C++对全局函数的处理方式与C有明显的不同。
     
    2.从标准头文件说起
      某企业曾经给出如下的一道面试题:
      面试题
      为什么标准头文件都有类似以下的结构?
        #ifndef __INCvxWorksh
        #define __INCvxWorksh
        #ifdef __cplusplus
        extern "C" {
        #endif
        /*...*/
        #ifdef __cplusplus
        }
        #endif
        #endif /* __INCvxWorksh */
      分析
      显然,头文件中的编译宏“#ifndef __INCvxWorksh、#define __INCvxWorksh、#endif” 的作用是防止该头文件被重复引用。
      那么
    #ifdef __cplusplus
    extern "C" {
     #endif
     #ifdef __cplusplus
    }
    #endif
      的作用又是什么呢?我们将在下文一一道来。
     
    3.深层揭密extern "C"
      extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。
      被extern "C"限定的函数或变量是extern类型的;
      extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。记住,下列语句:
      extern int a;
      仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。
      通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。
      与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。
      被extern "C"修饰的变量和函数是按照C语言方式编译和连接的;
      未加extern “C”声明时的编译方式
      首先看看C++中对类似C的函数是怎样编译的。
      作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:
    void foo( int x, int y );
      该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。
      _foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。
      同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。
      未加extern "C"声明时的连接方式
      假设在C++中,模块A的头文件如下:
    // 模块A头文件 moduleA.h
    #ifndef MODULE_A_H
    #define MODULE_A_H
    int foo( int x, int y );
    #endif
      在模块B中引用该函数:
    // 模块B实现文件 moduleB.cpp
    #include "moduleA.h"
    foo(2,3);
      实际上,在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!
      加extern "C"声明后的编译和连接方式
      加extern "C"声明后,模块A的头文件变为:
    // 模块A头文件 moduleA.h
    #ifndef MODULE_A_H
    #define MODULE_A_H
    extern "C" int foo( int x, int y );
    #endif
      在模块B的实现文件中仍然调用foo( 2,3 ),其结果是:
      (1)模块A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;
      (2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。
      如果在模块A中函数声明了foo为extern "C"类型,而模块B中包含的是extern int foo( int x, int y ) ,则模块B找不到模块A中的函数;反之亦然。
      所以,可以用一句话概括extern “C”这个声明的真实目的(任何语言中的任何语法特性的诞生都不是随意而为的,来源于真实世界的需求驱动。我们在思考问题时,不能只停留在这个语言是怎么做的,还要问一问它为什么要这么做,动机是什么,这样我们可以更深入地理解许多问题):
      实现C++与C及其它语言的混合编程。
    明白了C++中extern "C"的设立动机,我们下面来具体分析extern "C"通常的使用技巧。
     
      4.extern "C"的惯用法
      (1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:
    extern "C"
    {
    #include "cExample.h"
    }
      而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。
      笔者编写的C++引用C函数例子工程中包含的三个文件的源代码如下:
    /* c语言头文件:cExample.h */
    #ifndef C_EXAMPLE_H
    #define C_EXAMPLE_H
    extern int add(int x,int y);     //注:写成extern "C" int add(int , int ); 也可以
    #endif
    /* c语言实现文件:cExample.c */
    #include "cExample.h"
    int add( int x, int y )
    {
     return x + y;
    }
    // c++实现文件,调用add:cppFile.cpp
    extern "C"
    {
     #include "cExample.h"        //注:此处不妥,如果这样编译通不过,换成 extern "C" int add(int , int ); 可以通过
    }
    int main(int argc, char* argv[])
    {
     add(2,3);
     return 0;
    }
      如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { }。
      (2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。
      笔者编写的C引用C++函数例子工程中包含的三个文件的源代码如下:
    //C++头文件 cppExample.h
    #ifndef CPP_EXAMPLE_H
    #define CPP_EXAMPLE_H
    extern "C" int add( int x, int y );
    #endif
    //C++实现文件 cppExample.cpp
    #include "cppExample.h"
    int add( int x, int y )
    {
     return x + y;
    }
    /* C实现文件 cFile.c
    /* 这样会编译出错:#include "cExample.h" */
    extern int add( int x, int y );
    int main( int argc, char* argv[] )
    {
     add( 2, 3 );
     return 0;
    }
    展开全文
  • #include<stdio.h>...:standard library标准基本函数库,包涵CC++最常用的系统函数 #include<graphics.h>:图形库,分为像素函数、直线、线型、多变形,填充函数等 #include<math.h>...

    #include<stdio.h>:standard input&output标准输入输出头文件

    #include<stdlib.h>:standard library标准基本函数库,包涵C,C++最常用的系统函数

    #include<graphics.h>:图形库,分为像素函数、直线、线型、多变形,填充函数等

    #include<math.h>各种数学计算函数

    #include<time.h>日期与时间函数,计算运行时间

    #include<ctype.h>字符处理函数,字符的类别测试和字符的大小写转换

    #include<local.h>定义本地化函数,地区化,日期时间地区差异化转换

    #include<signal.h>信号处理

    #include<string.h>字符串的处理,用于字符串的合并查找比较等

    #include<iostream.h>数据流输入输出

    #include<assert.h>设定插入点

    #include<float.h>浮点数处理

    #include<errno.h>定义通过错误码来回报错误咨询,是调试程序的一个重要方法

    #include<conio.h>预处理命令,通过控制台进行数据输入和输出的函数,通过用户按键盘产生对应操作

    #include<mmsystem.h>windows中与多媒体有关的大多数接口

    PS:stdio.h和iostream.h的区别
    stdio.h是C语言的输入输出头文件,输入输出是printf(…);,scanf(…);
    iostream.h是C++的输入输出头文件,输入输出是cout<<…;,cin>>…;

    展开全文
  • C++编译头文件

    2006-07-26 14:04:00
    许多初学 VC 的朋友也许都为那么一个问题困扰过: 为什么所有的 cpp 都必须 #include "stdafx.h" 也许请教了别的高手之后,他们会告诉你,这是预编译头,必须包含。可是,这到底是为什么呢?预编译头有什么用呢?...

    许多初学 VC 的朋友也许都为那么一个问题困扰过:

        为什么所有的 cpp 都必须 #include "stdafx.h"

        也许请教了别的高手之后,他们会告诉你,这是预编译头,必须包含。可是,这到底
    是为什么呢?预编译头有什么用呢?

        这得从头文件的编译原理讲起。其实头文件并不神秘,它的全部作用,就是把自己的
    所有内容直接“粘贴”到相应的 #include 语句处。如果不相信的话,不妨做个实验,将
    一个 cpp 中的所有 #include 语句删掉,并将它包含的文件粘贴到相应的位置,你会发
    现,文件的编译和运行都完全没有受到影响。其实,编译器在编译你的程序的时候,所做
    的第一件事,也就是展开所有的 #include 语句和 #define 语句。

        头文件的出现,固然给书写程序带来了很大方便。可是到了 Windows 时代后,慢慢
    就呈现出一些问题了。几乎所有的 Windows 程序都必须包含 windows.h,而那个文件却
    硕大无比,将它展开后往所有文件中一粘贴,编译的时候立刻慢得像只蜗牛。

        到了 MFC 时代后,情况更为恶劣了。毕竟 C 风格的 Windows 头文件里面包含的还
    仅仅是函数定义和宏,编译难度不算太大,而 MFC 库里面的头文件可都是类声明啊!更
    何况,一个最简单的工程,都会生成大量的类,需要用到大量的函数。如果工程稍微复杂
    一些,编译难度可想而知!

        但是,人们惊奇地发现,虽然用到的头文件又多又杂,但是在一个工程中,总有那么
    一堆头文件,是几乎所有 cpp 都必须包含的。那么,可不可以把这些头文件提取出来,
    只编译一编,然后所有其它 cpp 就都能使用呢?没错,这就是预编译头的思想都由来!

        实践证明,使用了预编译头技术后,编译速度大大提高了。可以到你的工程目录下的
    Debug 或 Release 目录中看一看,里面有一个体积极为硕大的 .pch 文件,那就是传说
    中的“编译之后的预编译头”。

        使用了预编译头技术后,虽然带来了极大地方便,但也造成了一个问题:由于它假定
    预编译头中包含过的头文件会在所有 cpp 中使用,因此它在编译你的 cpp 的时候,就会
    将预编译头中已经编译完的部分加载到内存中。如果它突然发现你的 cpp 居然没有包含
    预编译头,它就会很郁闷,因为它不知道该如何将已编译完的部分从内存中请出去,整个
    编译过程就会失败。

        因此,如果你使用了预编译头技术,就必须在所有的 cpp 中包含预编译头。MFC 工
    程中为你建立了一个默认的预编译头 stdafx.h,如果你愿意,也可以在自己的工程中使
    用其它文件名作为你的预编译头,如果你觉得有必要。
     

     

    预编译头文件的使用 
    关键字:预编译,/Yu,/Yc,/Yx
    本文介绍VC6的预编译功能的使用,由于预编译详细使用比较的复杂,这里只介绍几个最重要的预编译指令: /Yu, /Yc,/Yx,/Fp。其它的详细资料可以参考:
          MSDN->Visual Studio D6.0Document -> Visual C++6.0 Document
             ->VC++ Programmer Guider ->Compiler and Linker
             ->Details->Creating Precompiled Header files

    预编译头的概念:
    所谓的预编译头就是把一个工程中的那一部分代码,预先编译好放在一个文件里(通常是以.pch为扩展名的),这个文件就称为预编译头文件这些预先编译好的代码可以是任何的C/C++代码--------甚至是inline的函数,但是必须是稳定的,在工程开发的过程中不会被经常改变。如果这些代码被修改,则需要重新编译生成预编译头文件。注意生成预编译头文件是很耗时间的。同时你得注意预编译头文件通常很大,通常有6-7M大。注意及时清理那些没有用的预编译头文件。
    也许你会问:现在的编译器都有Time stamp的功能,编译器在编译整个工程的时候,它只会编译那些经过修改的文件,而不会去编译那些从上次编译过,到现在没有被修改过的文件。那么为什么还要预编译头文件呢?答案在这里,我们知道编译器是以文件为单位编译的,一个文件经过修改后,会重新编译整个文件,当然在这个文件里包含的所有头文件中的东西(.eg Macro, Preprocessor )都要重新处理一遍。VC的预编译头文件保存的正是这部分信息。以避免每次都要重新处理这些头文件。
    预编译头的作用:
    方法一:手动方法
    根据上文介绍,预编译头文件的作用当然就是提高便宜速度了,有了它你没有必要每次都编译那些不需要经常改变的代码。编译性能当然就提高了。
    预编译头的使用:
         要使用预编译头,我们必须指定一个头文件,这个头文件包含我们不会经常改变的代码和其他的头文件,然后我们用这个头文件来生成一个预编译头文件(.pch文件)
     想必大家都知道 StdAfx.h这个文件。很多人都认为这是VC提供的一个“系统级别”的,编译器带的一个头文件。其实不是的,这个文件可以是任何名字的。我们来考察一个典型的由AppWizard生成的MFC Dialog Based 程序的预编译头文件。(因为AppWizard会为我们指定好如何使用预编译头文件,默认的是StdAfx.h,这是VC起的名字)。我们会发现这个头文件里包含了以下的头文件:
    #include <afxwin.h>         // MFC core and standard components
    #include <afxext.h>         // MFC extensions
    #include <afxdisp.h>        // MFC Automation classes
    #include <afxdtctl.h>             // MFC support for Internet Explorer 4 Common Controls
    #include <afxcmn.h>     
    这些正是使用MFC的必须包含的头文件,当然我们不太可能在我们的工程中修改这些头文件的,所以说他们是稳定的。
    那么我们如何指定它来生成预编译头文件。我们知道一个头文件是不能编译的。所以我们还需要一个cpp文件来生成.pch 文件。这个文件默认的就是StdAfx.cpp。在这个文件里只有一句代码就是:#include “Stdafx.h”。原因是理所当然的,我们仅仅是要它能够编译而已―――也就是说,要的只是它的.cpp的扩展名。我们可以用/Yc编译开关来指定StdAfx.cpp来生成一个.pch文件,通过/Fp编译开关来指定生成的pch文件的名字。打开project ->Setting->C/C++ 对话框。把Category指向Precompiled Header。在左边的树形视图里选择整个工程 (如图)
    (图1)
    在图中我们的Project Options(右下角的那个白的地方)可以看到 /Fp “debug/PCH.pch”,这就是指定生成的.pch文件的名字,默认的通常是 <工程名>.pch(我的示例工程名就是PCH)。
    然后,在左边的树形视图里选择StdAfx.cpp.如图:(图2)
    这时原来的Project Option变成了 Source File Option(原来是工程,现在是一个文件,当然变了)。在这里我们可以看到 /Yc开关,/Yc的作用就是指定这个文件来创建一个Pch文件。/Yc后面的文件名是那个包含了稳定代码的头文件,一个工程里只能有一个文件的可以有YC开关。VC就根据这个选项把 StdAfx.cpp编译成一个Obj文件和一个PCH文件。
       然后我们再选择一个其它的文件来看看,如图:
    在这里,Precomplier 选择了 Use ………一项,头文件是我们指定创建PCH 文件的stdafx.h
    文件。事实上,这里是使用工程里的设置,(如图1)/Yu”stdafx.h”。
       这样,我们就设置好了预编译头文件。也就是说,我们可以使用预编译头功能了。以下是注意事项:
    1):如果使用了/Yu,就是说使用了预编译,我们在每个.cpp文件的最开头,我强调一遍是最开头,包含 你指定产生pch文件的.h文件(默认是stdafx.h)不然就会有问题。如果你没有包含这个文件,就告诉你Unexpected file end. 如果你不是在最开头包含的,你自己试以下就知道了,绝对有很惊人的效果…..
    2)如果你把pch文件不小心丢了,根据以上的分析,你只要让编译器生成一个pch文件就可以了。也就是说把 stdafx.cpp(即指定/Yc的那个cpp文件)从新编译一遍就可以了。当然你可以傻傻的 Rebuild all。简单一点就是选择那个cpp文件,按一下Ctrl + F7就可以了。
    方法二。自动使用
    很简单只要指定/YX就可以了。或者在上图中选择Automatic………就可以了。注意的事情是如果你指定了/Yc /Yu的话,/Yx是会被忽略的。前者的优先级别高一些。

    展开全文
  • C++编程时如果多个类引用同一个头文件,有可能会产生二次编译的错误,比如公共头文件common.h被CFile1.h和CFile2.h引用,在主函数CFile类中同时引用CFile1.h和CFile2.h时,编译器会报如下错误:text\common.h(3): ...

    C++编程时如果多个类引用同一个头文件,有可能会产生二次编译的错误,比如公共头文件common.h被CFile1.h和CFile2.h引用,在主函数CFile类中同时引用CFile1.h和CFile2.h时,编译器会报如下错误:text\common.h(3): error C2011: “common”:“class”类型重定义

    为了解决以上问题,C++有两种防止头文件被二次编译的方式。

    第一种方法采用#pragma once,属于编译器预编译命令。

    该方法的优点:避免名字冲突

    该方法的缺点:与编译器相关,较早的编译器不支持该方法,且该方法不适用跨平台程序。

    并且如果某个头文件有多份拷贝,该方法不能保证不被重复包含。


    另一种是采用宏定义

    #ifndef XXXX

    #define XXXX

    //program codes

    #endif XXXX

    该方法的优点:语言支持,移植性好可用于跨平台的程序

    该方法的缺点:宏名字不能冲突,比如:

    CFile1头文件中

    #ifndef __FILE_H__

    #define __FILE_H__

    //program codes

    #endif 

    CFile2头文件中也定义了一样的宏。

    当在主函数CFile中引用以上两个文件

    #include "File1.h"

    #include "File2.h"

    .................

    预编译阶段把File1文件展开,获得宏__FILE_H__,当处理File2文件中发现宏__FILE_H__已经存在,则不会在包含File2文件。

    并且由于编译器每次都必须打开头文件才能判断是否有重复定义,在大型项目中,宏定义的方法会使得编译时间相对较长。




    展开全文
  • c++中的头文件循环引用 问题 在项目文件变多时,由于组织不当,很容易出现头文件的循环引用 有时候虽然没有出现循环引用,但是头文件多处被include,导致编译速度变慢 解决办法 适当的使用前置声明 什么...

    c++中的头文件循环引用

    问题

    1. 在项目文件变多时,由于组织不当,很容易出现头文件的循环引用
    2. 有时候虽然没有出现循环引用,但是头文件多处被include,导致编译速度变慢

    解决办法

    适当的使用前置声明

    • 什么是前置声明

        就是当你只用到某个类型的指针或者引用时,可以不用把整个类型的头文件include进来。只需要声明这个类型即可

    • why

        因为在只需要使用这个类的指针和引用时,编译器编译此文件时就不需要知道这个类型的内存布局。编译器只需要把指针或者引用翻译成地址即可。

    例子

    #ifndef __A_H__
    #define __A_H__
    #include "B.h"
    class A{
            int f1;
            int f2;
            B *f3;
    };
    #endif
    //A.h (END) 
    #ifndef __B_H__
    #define __B_H__
    #include "A.h"
    class B{
        A a;
    };
    #endif
    //B.h (END) 
    #include "A.h"
    int main(int argc,char** argv)
    {
            A a;
            return 0;
    }
    //main.cpp (END) 

    我们用g++  main.cpp编译,会发现编译出错

    In file included from A.h:3:0,
                     from main.cpp:1:
    B.h:5:5: error: 'A' does not name a type
         A a;

    这里就出现了头文件循环引用,main引用了A.h, 而A.h引用了B.h,而B.h又引用了A.h。。这是无法通过编译的。

    但是观察代码可以发现 在A.h中其实没必要include B.h的。因为A.h只是用了B的指针,这里只需要把include “B.h”去掉然后换成class B; 即可通过编译

    #ifndef __A_H__
    #define __A_H__
    class B;
    class A{
            int f1;
            int f2;
            B *f3;
    };

    模板类的前置声明

    模板类的前置声明语法是

    template<typename T> class A;
    展开全文
  • ----C++头文件相互引用,一个#include,另一个class C C++头文件相互#include时最好是: (1)在"CA.h"中 #include "CB.h". (2)在"CB.h"中用类的前向声明: class CA; (3)最好加上...
  • C++程序时,不要头文件中包含太多其他的头文件,除非绝对必要。 通常我们在头文件中声明类,在cpp文件中实现该类的函数。 如果成员函数的参数是别的类的声明或引用,则可能会直接包含该类的声明对应的头文件。 ...
  • 由于种fei种chang原lan因duo,至今没写过什么大项目,每次项目里都只有那么两个...修改自 CSDN - C++头文件包含注意要点 方法 在引用一个符号(包括变量,函数,结构,类等)之前确保它已经声明或者已经定义。 ...
  • 而.h文件则被称作C++头文件,里面放的也是C++的源代码。 C+ +语言支持“分别编译”(separate compilation)。也就是说,一个程序所有的内容,可以分成不同的部分分别放在不同的.cpp文件里。.cpp文件里的东西
  • 而.h文件则被称作C++头文件,里面放的也是C++的源代码。C+ +语言支持“分别编译”(separate compilation)。也就是说,一个程序所有的内容,可以分成不同的部分分别放在不同的.cpp文件里。.cpp文件里的东西都是相对...
  • c++如何写头文件.h

    2017-10-31 17:16:02
    按照本人现在的应用水平删减改编自博客:http://blog.csdn.net/guoyong10721073/article/details/25245293一、C++编译模式通常,在一...而.h文件则被称作C++头文件,里面放的也是C++的源代码。 C+ +语言支持“分别编译
  • C++编译时,教科书中写道:#include “headfile.h”优先在当前目录查找头文件;#include 从系统默认路径查找头文件。先前以为系统默认路径是环境变量$PATH指定的路径,在系统上一查,傻了眼: -bash-3.2$ echo$PATH ...
  • C++编译头文件简介

    2014-03-21 14:27:26
    所谓头文件编译,就是把一个工程(Project)中使用的一些标准头文件(如Windows.H、stdio.h)或者那些稳定的,不会被频繁修改的代码或者其它头文件预先编译,以后编译该工程时,不再编译这部分代码和头文件,仅仅使用...
  • Linux中C/C++编译添加头文件和库路径方式
  • c/c++头文件引用顺序

    2018-12-10 15:55:42
        一.《Google C++ 编程风格指南》里的观点   ... 最近公司在推行编码规范,领导提议基本上使用《Google C++ 编程风格...其中《Google C++ 编程风格指南》对于头文件的包含顺序是这样的:   Names and ...
  • C++中同一个头文件被多个文件包含,需要在此头文件中加入 #ifndef _*&* #define _*&* .... #endif 以此保证次头文件编译过程中只被编译一次,不会产生重复编译的现象。
  • 一、详解编译、链接 有些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错误信息不能定位到某一行)。或者对语言的一些部分不知道为什么...
  • 函数被C++编译后在库中的名字与C语言的不同,C++C是两种完全不同的编译链接处理方式,如果直接在C++里面调用C函数,会找不到函数体,报链接错误,解决办法:加 extern “C”,示例如下: VS2015新建win32...
  • 但现在看起来真不爽,main.c依赖的文件不能在工程中管理,要修改那些文件的话麻烦大了。如何既能分成多个文件又能在工程中看到呢?可以,使用头文件(h文件)
1 2 3 4 5 ... 20
收藏数 180,609
精华内容 72,243