C++ 关于头文件和源文件 编译和链接问题

本文解释了头文件在C/C++编程中的作用,包括如何正确使用头文件,避免重复定义问题,以及可以在头文件中定义的元素类型。文章还讨论了不同类型的链接方式及其对程序的影响。

 所有的头文件,不会被编译, #include XXX.h的意义是    把某个头文件的内容全部都搬进源文件里面。

 头文件里面不能 单独声明 一个  int a; char b; 这种变量。 因为在#include 后,会有多个定义。  

  定义永远只能有一个,但是声明可以有很多个。 所以在链接后,如果多个cpp文件#include后,会有多个定义。(这种叫外部链接)  链接的时候不知道以哪个链接为准。

 还有的东西是内部链接,那么定义的东西只会在内部生效,链接的时候不会出现上面的问题。 


const int a = 5;

inline void PP() {
//cout << "nihao" << endl;
};


static int b ;


class A  


以上4种可以在.h文件中定义。 但是注意A中如果有函数,一定要在A类里面写出函数的实现。

编译头文件今天在改一个很大的程序,慢慢看,慢慢改。突然发现一个.c文件,里面什么也没有,就几个头文件,我一看,我靠,这不是把简单的问题搞复杂了吗,随手删掉那个c文件。结果不能编译了,我靠:fatal error C1083: Cannot open precompiled header file: \'Debug/v13_3.pch\':No such file or directory怎么rebuild all都不行。上网查了一下,才搞懂了:----------------总结------如果工程很大,头文件很多,而有几个头文件又是经常要用的,那么1。把这些头文件全部写到一个头文件里面去,比如写到preh.h2。写一个preh.c,里面只一句话:#include "preh.h"3。对于preh.c,在project setting里面设置creat precompiled headers,对于其他.c文件,设置use precompiled header file//哈哈我试了一下,效果很明显,不用precompiled header,编译一次我可以去上个厕所,用precompiled header,编译的时候,我可以站起来伸个懒腰,活动活动就差不多啦---------转载的文章----------预编译头的概念:所谓的预编译头就是把一个工程中的那一部分代码,预先编译好放在一个文件里(通常是以.pch为扩展名的),这个文件就称为预编译头文件这些预先编译好的代码可以是任何的C/C++代码--------甚至是inline的函数,但是必须是稳定的,在工程开发的过程中不会被经常改变。如果这些代码被修改,则需要重新编译生成预编译头文件。注意生成预编译头文件是很耗时间的。同时你得注意预编译头文件通常很大,通常有6-7M大。注意及时清理那些没有用的预编译头文件。也许你会问:现在的编译器都有Time stamp的功能,编译器在编译整个工程的时候,它只会编译那些经过修改的文件,而不会去编译那些从上次编译过,到现在没有被修改过的文件。那么为什么还要预编译头文件呢?答案在这里,我们知道编译器是以文件为单位编译的,一个文件经过修改后,会重新编译整个文件,当然在这个文件里包含的所有头文件中的东西(.eg Macro, Preprocesser )都要重新处理一遍。VC的预编译头文件保存的正是这部分信息。以避免每次都要重新处理这些头文件。预编译头的作用:根据上文介绍,预编译头文件的作用当然就是提高便宜速度了,有了它你没有必要每次都编译那些不需要经常改变的代码。编译性能当然就提高了。预编译头的使用:要使用预编译头,我们必须指定一个头文件,这个头文件包含我们不会经常改变的代码其他的头文件,然后我们用这个头文件来生成一个预编译头文件(.pch文件)想必大家都知道 StdAfx.h这个文件。很多人都认为这是VC提供的一个“系统级别”的,编译器带的一个头文件。其实不是的,这个文件可以是任何名字的。我们来考察一个典型的由AppWizard生成的MFC Dialog Based 程序的预编译头文件。(因为AppWizard会为我们指定好如何使用预编译头文件,默认的是StdAfx.h,这是VC起的名字)。我们会发现这个头文件里包含了以下的头文件:#include // MFC core and standard components#include // MFC extensions#include // MFC Automation classes#include // MFC support for Internet Explorer 4Common Controls#include 这些正是使用MFC的必须包含的头文件,当然我们不太可能在我们的工程中修改这些头文件的,所以说他们是稳定的。那么我们如何指定它来生成预编译头文件。我们知道一个头文件是不能编译的。所以我们还需要一个cpp文件来生成.pch 文件。这个文件默认的就是StdAfx.cpp。在这个文件里只有一句代码就是:#include “Stdafx.h”。原因是理所当然的,我们仅仅是要它能够编译而已?D?D?D也就是说,要的只是它的.cpp的扩展名。我们可以用/Yc编译开关来指定StdAfx.cpp来生成一个.pch文件,通过/Fp编译开关来指定生成的pch文件的名字。打开project ->Setting->C/C++ 对话框。把Category指向Precompiled Header。在左边的树形视图里选择整个工程 Project Options(右下角的那个白的地方)可以看到 /Fp “debug/PCH.pch”,这就是指定生成的.pch文件的名字,默认的通常是 .pch(我的示例工程名就是PCH)。然后,在左边的树形视图里选择StdAfx.cpp.//这时只能选一个cpp文件!这时原来的Project Option变成了 Source File Option(原来是工程,现在是一个文件,当然变了)。在这里我们可以看到 /Yc开关,/Yc的作用就是指定这个文件来创建一个Pch文件。/Yc后面的文件名是那个包含了稳定代码的头文件,一个工程里只能有一个文件的可以有YC开关。VC就根据这个选项把 StdAfx.cpp编译成一个Obj文件一个PCH文件。然后我们再选择一个其它的文件来看看,//其他cpp文件在这里,Precomplier 选择了 Use ???一项,头文件是我们指定创建PCH 文件的stdafx.h文件。事实上,这里是使用工程里的设置,(如图1)/Yu”stdafx.h”。这样,我们就设置好了预编译头文件。也就是说,我们可以使用预编译头功能了。以下是注意事项:1):如果使用了/Yu,就是说使用了预编译,我们在每个.cpp文件的最开头,我强调一遍是最开头,包含 你指定产生pch文件的.h文件(默认是stdafx.h)不然就会有问题。如果你没有包含这个文件,就告诉你Unexpected file end. 如果你不是在最开头包含的,你自己试以下就知道了,绝对有很惊人的效果?..fatal error C1010: unexpected end of file while looking for precompiledheader directiveGenerating Code...2)如果你把pch文件不小心丢了,编译的时候就会产生很多的不正常的行为。根据以上的分析,你只要让编译器生成一个pch文件。也就是说把 stdafx.cpp(即指定/Yc的那个cpp文件)从新编译一遍。当然你可以傻傻的 Rebuild All。简单一点就是选择那个cpp文件,按一下Ctrl + F7就可以了。不然可是很浪费时间的哦。//呵呵,如果你居然耐着性子看到了这里,那么再回到帖子最开始看看我的总结吧!
<think>首先,用户的问题是:“我想理解C++头文件源文件的区别请问C++头文件源文件的区别”。我需要基于系统级指令用户可见层的指导来回答。系统级指令:-所有行内数学表达式必须使用$...$格式。-独立公式必须使用$$...$$格式并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-回答中引用的段落末尾自然地添加引用标识,例如:[^1]用户可见层:-回答结构清晰,帮助用户逐步解决问题。-尽量保证回答真实可靠。-参考站内引用:引用[1]、[2]、[3]提供了相关信息,但需要基于用户问题整合。用户问题:理解C++头文件源文件的区别。关键点来自引用:-引用[1]:头文件用于存放声明(如类型定义、函数声明、全局变量声明),作为对外接口;源文件存放实现(如函数体、全局变量定义)。头文件随二进制库发布,源代码保留。-引用[2]:头文件(如.h)源文件(如.cpp)的区别;在C++中,.hpp对应.cpp以区分语言;头文件内容被复制到源文件中;定义全局函数变量时要注意。-引用[3]:类似引用[1],强调头文件用于声明,源文件用于实现。我的回答需要结构化:1.**介绍区别**:清晰解释头文件源文件的作用。2.**详细说明**:逐步解释声明实现的概念。3.**例子**:提供代码示例来阐明。4.**引用整合**:自然地融入引用信息。5.**生成相关问题**:在末尾添加相关问题。由于用户问题不涉及数学表达式,我可以避免使用LaTeX,除非有必要。但指令说“所有行内数学表达式必须使用$...$格式”,所以如果有数学内容,才使用。这里可能不需要。回答结构:-**开头**:直接回答问题。-**主体**:-解释头文件的作用。-解释源文件的作用。-对比区别。-使用代码示例。-**结尾**:总结,并生成相关问题。生成相关问题:基于主题,创建2-3个相关问题,例如:1.为什么在C++中要分开头文件源文件?2.如何正确在头文件中使用预编译指令?3.头文件源文件编译过程中如何交互?引用标识:在回答中,当引用信息时,添加[^1]、[^2]、[^3]。例如,提到声明在头文件时,引用[^1]。确保中文回答。最终输出:-回答部分。-§§相关问题§§部分。</think>### C++头文件源文件的区别 在C++编程中,头文件(通常以`.h`或`.hpp`为扩展名)源文件(通常以`.cpp`为扩展名)是两种核心的源代码文件,它们共同协作以实现代码的组织模块化。它们的核心区别在于**功能分工**:头文件主要用于**声明**(如函数原型、类型定义、全局变量声明等),作为代码的接口;而源文件主要用于**实现**(如函数体、变量定义等),存放具体的逻辑代码。这种分离设计提高了代码的可维护性、可重用性编译效率。下面我将逐步解释其区别实际应用。 #### 1. **头文件的作用** - **核心功能**:头文件存放对实体的**声明**,包括函数原型、类定义、全局变量声明、模板声明等。这些声明不包含具体的实现细节,只提供接口信息,供其他文件引用。例如,一个头文件可以声明一个函数,但不定义其内部逻辑。 - **为什么需要头文件**:在C++中,每个源文件(`.cpp`)在编译时是独立的。头文件通过`#include`预编译指令被复制到源文件中,确保编译器在编译多个源文件时能访问一致的声明,避免重复定义错误。这类似于提供一个“蓝图”或“目录”,让不同源文件能相互调用函数或共享类型定义。 - **实际例子**: - 头文件 `math_utils.h` 可能包含: ```cpp #ifndef MATH_UTILS_H // 预编译指令防止重复包含 #define MATH_UTILS_H // 声明一个函数:计算平方 int square(int x); #endif ``` - 这里,`int square(int x);` 是一个函数声明,但没有实现细节。头文件作为接口,允许其他源文件调用 `square` 函数[^1]。 #### 2. **源文件的作用** - **核心功能**:源文件存放实体的**实现**,包括函数体、全局变量定义、类成员函数的实现等。它提供了声明的具体逻辑,确保程序在运行时能执行预期的操作。 - **为什么需要源文件**:源文件编译的基本单位。编译器将每个源文件单独编译为目标文件(`.obj`或`.o`),然后链接器将它们合并为可执行程序。源文件通过`#include`引入头文件来获取声明,避免在多个文件中重复实现同一实体,从而减少错误提高效率。 - **实际例子**: - 源文件 `math_utils.cpp` 对应上述头文件: ```cpp #include "math_utils.h" // 引入头文件声明 // 实现 square 函数 int square(int x) { return x * x; // 具体逻辑 } ``` - 这里,源文件定义了 `square` 函数的具体行为。编译编译文件时,会依赖头文件中的声明来验证函数签名[^3]。 #### 3. **关键区别对比表** 下表总结了头文件源文件的主要差异: | 特性 | 头文件(.h/.hpp) | 源文件(.cpp) | |--------------|---------------------------------------|-------------------------------------| | **主要目的** | 存放声明(接口) | 存放实现(逻辑) | | **内容示例** | 函数原型、类定义、宏定义、extern变量 | 函数体、全局变量定义、静态变量 | | **编译角色** | 通过`#include`被复制到源文件中 | 独立编译为目标文件 | | **重用性** | 高(可作为库接口发布) | 低(实现通常不公开) | | **文件扩展名**| `.h`(C/C++通用)或`.hpp`(C++专用) | `.cpp`(C++)或`.cc` | | **错误风险** | 重复包含可能导致编译错误 | 重复定义可能导致链接错误 | - **为什么分开?**:这种分离允许团队协作:开发者只需共享头文件作为API,而隐藏源文件实现细节。例如,商业库发布时,提供头文件二进制库文件(如`.dll`或`.so`),保护源代码[^1]。 - **语言区分**:在混合CC++项目中,常用`.h`对应C头文件,`.hpp`对应C++头文件,以避免语言混淆(如C++的函数重载特性在C中不支持)[^2]。 - **全局实体处理**:全局变量或函数在头文件中应使用`extern`关键字声明(例如 `extern int globalVar;`),而在源文件中定义(例如 `int globalVar = 10;`),否则可能引发链接错误[^2]。 #### 4. **实际开发中的注意事项** - **避免常见错误**: - 不要在头文件中定义函数或变量(除非使用`inline`或模板),否则多个源文件包含同一头文件时会导致重复定义错误。 - 使用预编译指令(如 `#ifndef ... #define ... #endif`)防止头文件被多次包含。 - **编译过程示例**: - 当编译 `main.cpp`(包含 `#include "math_utils.h"`)时,预处理器将头文件内容复制到 `main.cpp` 中。 - 编译器分别编译 `main.cpp` `math_utils.cpp` 为目标文件。 - 链接器将目标文件合并,解析函数调用(如 `square` 的实现来自 `math_utils.cpp`)。 - **好处**:这种结构支持模块化开发,便于单元测试代码重用。例如,修改实现(源文件)时,不需要重新编译所有依赖头文件的代码。 总结来说,C++头文件源文件的区别本质上是**接口与实现的分离**:头文件定义“做什么”(声明),源文件定义“怎么做”(实现)。这种设计是C++模块化的基石,能显著提升大型项目的可管理性[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值