源文件和头文件
CC++中的源文件与头文件的区别
CC++中的源⽂件与头⽂件的区别问题提出的背景:最近在⾃⼰动⼿,⽤C来实现各类经典算法,还搬到了Github上,但是有⼀个问题⽐较困扰我,就是这些可以复⽤的,作为⼯具⽅法的算法,究竟应该放在头⽂件还是源⽂件⾥?⼀般的、通⽤的准则到底是什么呢?或者说头⽂件与源⽂件的作⽤究竟是什么?在编译连接等过程中,编译器会对他们有怎样的区别对待呢?⼀、实现究竟放在哪⾥?⾸先回答第⼀个问题,⼀般来说,什么时候需要把实现放在头⽂件⾥,什么时候⼜需要把实现放在源⽂件⾥?(此部分参考:)不把实现放在头⽂件中,往往是出于以下⼏种顾虑:1、暴露了实现细节2、头⽂件被包含到不同的源⽂件中,会导致链接冲突3、头⽂件被包含到不同的源⽂件中,会导致有多份实现被编译出来,增⼤可执⾏体的体积如果有顾虑 1 ,那很显然应该在第⼀时间抛弃完全在头⽂件中实现的念头。
⾄于顾虑 2和3 的,我们举例如下。
例如有以下头⽂件 c_function.h:int integer_add(const int a, const int b){return a + b;}如果在同⼀⼯程中,有 a.c 和 b.c 两个(或两个以上)源⽂件包含了此头⽂件,则在链接时期就会发⽣冲突,因为在两个源⽂件编译得到的⽬标⽂件中都有⼀份 integer_add 的函数实现,导致链接器不知道对于调⽤了此函数的调⽤者,应该使⽤哪⼀个副本。
解决冲突办法有两个,⼀个是加上 inline ,另⼀个是加上 static 。
使⽤这两个关键字的任意⼀个来修饰 integer_add 函数,然⽽本质却⼤不相同。
如果使⽤ inline ,则意味着编译器会在调⽤此函数的地⽅把函数的⽬标代码直接插⼊,⽽不是放置⼀个真正的函数调⽤,实际作⽤就是这个函数事实上已经不再存在,⽽是像宏⼀样被就地展开了。
使⽤ inline 的副作⽤,⾸先在于⽏庸置疑地,代码的体积变⼤了;其次则是,这个关键字严格算起来并不是 C 语⾔的关键字,使⽤它多少会带来⼀些移植性⽅⾯的风险。
c++编译顺序
c++编译顺序c++编译顺序是指c++程序从源代码到可执行文件的转换过程中,各个源文件和目标文件的生成和处理的先后顺序。
c++程序一般由多个源文件(.cpp)和头文件(.h)组成,每个源文件都需要经过预处理(preprocess),编译(compile),汇编(assemble)和链接(link)四个阶段,才能最终生成可执行文件(.exe)。
不同的源文件之间可能存在依赖关系,比如一个源文件调用了另一个源文件中定义的函数或变量,或者一个源文件包含了另一个头文件中声明的内容。
因此,c++编译顺序需要考虑这些依赖关系,保证每个源文件在被处理之前,它所依赖的其他源文件或头文件已经被正确地处理过。
原理c++编译是基于c++程序的运行原理和编译器的工作原理。
c++程序的运行原理是基于计算机硬件和操作系统的,计算机只能执行机器语言指令,而操作系统负责加载可执行文件到内存,并调用入口函数开始执行。
因此,c++程序需要将人类可读的高级语言代码转换为机器可执行的二进制代码,并且按照操作系统规定的格式组织成可执行文件。
这个转换过程就是由编译器完成的。
编译器是一种软件,它可以将一种语言(源语言)翻译成另一种语言(目标语言)。
为了提高翻译效率和质量,编译器一般分为多个模块,每个模块负责完成一部分翻译工作,并生成中间结果。
这些中间结果可以是文本文件,也可以是二进制文件。
最后一个模块负责将所有中间结果合并成最终结果。
这些模块之间也有依赖关系,后面的模块需要使用前面模块生成的中间结果作为输入。
步骤c++编译可以分为四个阶段:预处理(preprocess),编译(compile),汇编(assemble)和链接(link)。
每个阶段都会生成对应的中间结果,并且有自己的工作内容和注意事项。
下面分别介绍每个阶段的具体内容。
预处理预处理是指对源代码进行一些文本替换和拷贝操作,以便于后续阶段进行语法分析和翻译。
预处理主要完成以下工作:-处理预处理指令(preprocessor directive),即以#开头的指令,比如#include, #define, #ifdef, #endif等。
C++中头文件(.h)和源文件(.cpp)都应该写些什么
C++中头⽂件(.h)和源⽂件(.cpp)都应该写些什么头⽂件(.h):写类的声明(包括类⾥⾯的成员和⽅法的声明)、函数原型、#define常数等,但⼀般来说不写出具体的实现。
在写头⽂件时需要注意,在开头和结尾处必须按照如下样式加上预编译语句(如下):#ifndef CIRCLE_H#define CIRCLE_H//你的代码写在这⾥#endif这样做是为了防⽌重复编译,不这样做就有可能出错。
⾄于CIRCLE_H这个名字实际上是⽆所谓的,你叫什么都⾏,只要符合规范都⾏。
原则上来说,⾮常建议把它写成这种形式,因为⽐较容易和头⽂件的名字对应。
源⽂件(.cpp):源⽂件主要写实现头⽂件中已经声明的那些函数的具体代码。
需要注意的是,开头必须#include⼀下实现的头⽂件,以及要⽤到的头⽂件。
那么当你需要⽤到⾃⼰写的头⽂件中的类时,只需要#include进来就⾏了。
下⾯举个最简单的例⼦来描述⼀下,咱就求个圆⾯积。
第1步,建⽴⼀个空⼯程(以在VS2003环境下为例)。
第2步,在头⽂件的⽂件夹⾥新建⼀个名为Circle.h的头⽂件,它的内容如下:#ifndef CIRCLE_H#define CIRCLE_Hclass Circle{private:double r;//半径public:Circle();//构造函数Circle(double R);//构造函数double Area();//求⾯积函数};#endif注意到开头结尾的预编译语句。
在头⽂件⾥,并不写出函数的具体实现。
第3步,要给出Circle类的具体实现,因此,在源⽂件夹⾥新建⼀个Circle.cpp的⽂件,它的内容如下:#include "Circle.h"Circle::Circle(){this->r=5.0;}Circle::Circle(double R){this->r=R;}double Circle:: Area(){return 3.14*r*r;}需要注意的是:开头处包含了Circle.h,事实上,只要此cpp⽂件⽤到的⽂件,都要包含进来!这个⽂件的名字其实不⼀定要叫Circle.cpp,但⾮常建议cpp⽂件与头⽂件相对应。
C语言头文件源文件
C语⾔头⽂件源⽂件C语⾔头⽂件源⽂件1、头⽂件与源⽂件头⽂件⽤于声明接⼝函数,格式如下如创建test.h#ifndef _TEST_H_#define _TEST_H_/*接⼝函数的申明*/#endif#ifndef _TEST_H_#define _TEST_Hint sum(int x, int y);void swap(int *x, int *y);int max(int x, int y);#endif源⽂件⽤于接⼝函数的实现,源⽂件中只写接⼝函数的实现不能写main()函数#include <stdio.h>#include "test.h"int sum(int x, int y){return x+y;}void swap(int *x, int *y){int tmp;tmp = *x;*x = *y;*y = tmp;}int max(int x, int y){return (x>y)? x : y;}2、⽤户⽂件头⽂件和源⽂件⼀般是标准库⽂件或者⾃定义的库⽂件,⽤户⽂件则是我们⾃⼰写的⽂件,我们需要在⽤户⽂件中使⽤库⽂件或函数,就要包含所需的头⽂件#include <stdio.h>#include "test.h"int main(){int a = 1, b = 2;swap(&a, &b);printf("sum(%d,%d)=%d\n", a, b, sum(a, b));printf("a=%d, b=%d\n", a, b);printf("max(%d,%d)=%d\n", a, b, max(a, b));return0;}3、多⽂件编译当我们使⽤的时候,如果只编译main.c(gcc main.c)就会报错原因是在test.h中找不到函数的实现,所以在编译时要将源⽂件test.c和main.c⼀起编译(gcc main.c test.c),这样就不会报错4、makefile和shell脚本当我们包含的头⽂件特别多,在编译时就要编译很多源⽂件(gcc main.c test1.c test2.c test3.c test4.c ... testn.c),这样就会⾮常长,所以我们可以将命令⾏写到脚本⾥⾯进⾏批处理(1)shell脚本创建⼀个build.sh的脚本⽂件,然后将需要编译的命令⾏写到脚本⽂件⾥,编译时输⼊命令 sh build.sh就完成了编译(2)makefile(待续。
VS2008教程
for(;;) {
pos1 = pos2 = 0;
if((pos1 = tmpstr.find_first_not_of(delimiters, pos2))
== string::npos)
break;
if((pos2 = tmpstr.find_first_of(delimiters, pos1))
}
return fields.size();
}
函数声明可以放在任何一个调用它的函数之前,而且在调用一个函数之前必须在调用者函数之前定义或声明被调函数。函数的定义只能有一次,如果调用者与被调用者不在同一编译单元,只能在调用者之前添加函数的声明。函数定义只能有一次,函数声明可以有无限次(理论上),这也是头文件的作用,将一批函数的声明放入一个头文件中,在任何需要这些函数声明的地方引用该头文件,以便于维护。
1.1.1.4. 小结
从理论上讲,声明与定义的区别就是:定义描述了内部内容,而声明不表露内部内容,只说明对外接口。例如,类的定义包含了内部成员的声明,而类的声明不包含任何类的内部细节;函数的定义包含了函数体,而函数声明只包括函数的签名;变量的定义可以包含初始化,而变量的声明不可以包含初始化。
class B { public: A* CreateA( void ) const; }
类的定义只给出了类包含了哪些数据(成员变量)和接口(成员函数),但并没有给出实现,程序的实现应该放在原代码文件中。如例程[2-1]中的Point类定义在Point.hpp头文件中,相应的源代码文件Point.cpp的内容如例程[2-4]所示。
// 例程2-2: 类的声明
class Point;
类的说明与实现都可以放在头文件中,因为上层代码需要使用Point的类必须知道当前工程已经定义了这个类。但应该使用定义还是声明呢?使用声明可以的地方使用定义都是可以的,但是,过多得使用定义会使项目编译时间加长,减慢编译速度,细节可参见(@see effective series,item 34)。
c语言中头文件和源文件解析 编译原理
c语言中头文件和源文件解析编译原理头文件和源文件是C语言中常见的两种文件类型,它们在编译原理中扮演着重要的角色。
本文将对头文件和源文件进行解析,从编译原理的角度探讨它们的作用和使用方法。
一、头文件的概念和作用头文件是一种特殊的文件,它通常以.h作为文件扩展名,用于存放函数声明、宏定义、结构体声明等内容。
头文件的作用主要有以下几个方面:1.1 提供接口声明头文件中包含了函数的声明,通过包含头文件可以让源文件知道这些函数的存在,并且能够正确地调用这些函数。
这种方式可以提高代码的可读性和可维护性,使得不同的源文件可以共享同一个函数的实现。
1.2 定义常量和宏头文件中可以定义常量和宏,这些常量和宏可以被多个源文件引用和使用。
通过在头文件中定义常量和宏,可以提高代码的可重用性和可维护性,避免了在多个源文件中重复定义常量和宏的问题。
1.3 声明结构体和类型头文件中可以声明结构体和类型,这些结构体和类型可以被多个源文件引用和使用。
通过在头文件中声明结构体和类型,可以提高代码的可读性和可维护性,避免了在多个源文件中重复声明结构体和类型的问题。
二、源文件的概念和作用源文件是C语言程序的主要组成部分,它通常以.c作为文件扩展名,包含了具体的函数实现和全局变量定义等内容。
源文件的作用主要有以下几个方面:2.1 实现函数的定义源文件中包含了函数的具体实现,通过编译和链接的过程,可以将函数的定义和函数的调用联系起来。
源文件中的函数实现可以直接访问和修改全局变量,同时也可以调用其他源文件中的函数。
2.2 定义全局变量源文件中可以定义全局变量,这些全局变量可以被多个函数访问和修改。
全局变量在程序的整个执行过程中都是存在的,它们的作用域不限于某个函数,可以在不同的函数之间共享数据。
2.3 包含头文件源文件可以通过包含头文件来使用头文件中定义的函数、常量、宏、结构体和类型等。
通过包含头文件,源文件可以获取到头文件中的声明信息,从而可以正确地使用其中定义的内容。
头文件与源文件在c语言中应用简单示例
一、概述C语言作为一种被广泛使用的程序设计语言,其核心概念之一就是头文件(Header File)和源文件(Source File)。
头文件和源文件在C 语言中的应用非常普遍,它们的合理使用对于提高代码的可读性、可维护性和可重用性,起着非常重要的作用。
本文将从头文件和源文件的概念入手,通过简单的示例帮助读者更加深入的理解并应用头文件与源文件在C语言中的重要性和用法。
二、头文件与源文件概念与作用1. 头文件(Header File)是一种特殊的文本文件,它以“.h”为扩展名,用来包含要被其他文件引用的声明和定义,通常包含函数原型、宏定义、数据结构等内容。
当程序需要使用某些外部的函数或数据结构时,可以通过#include指令引用相应的头文件。
2. 源文件(Source File)是包含C语言源代码的文件,通常以“.c”为扩展名。
源文件包含了程序的实际代码,其中定义了各种函数、变量、数据结构等。
源文件中也可以通过#include指令引用头文件,以便在源文件中使用头文件中声明的函数和数据结构。
三、头文件的编写与应用1. 定义头文件的格式头文件通常包括以下内容:- 头文件保护宏(Header Guard):用来防止头文件被多次引用的问题,通常采用#ifndef、#define和#endif三个宏来实现;- 函数原型:声明函数的名称、返回类型和参数列表,以便在源文件中使用;- 宏定义:定义一些常量和宏,方便程序中的代码使用。
2. 编写简单的头文件示例以一个简单的数学计算为例,定义一个头文件math.h包含两个函数的声明:```c#ifndef MATH_H#define MATH_Hint add(int a, int b);int subtract(int a, int b);#endif```3. 应用头文件在源文件中使用这个头文件:```c#include "math.h"int m本人n() {int result1 = add(10, 5);int result2 = subtract(10, 5);// other code...return 0;}```四、源文件的编写与应用1. 定义源文件的格式源文件中包括了实际的函数定义和全局变量定义,以及程序的入口函数m本人n()定义。
C源文件要包含自己的头文件
引言:我们经常在c工程中发现,源文件中要包含自己的头文件。
一直以来,都不知道为什么这样做。
现在,我知道了。
以前的认知:我认为,.c文件没有必要包含自己的.h文件。
.h文件包含.c文件中定义的函数和全局变量的声明,.h文件就是.c文件提供的对外接口文件。
既然.h文件就是.c文件提供的对外接口文件,那么.c文件就没必要包含自己的.h文件了(.h文件是对外提供用的,对内又何必再包含进来呢)。
鉴于这样的理解,我对于工程中.c源文件包含自己的.h头文件很是不理解,不知道为什么要这样做。
现在对此的理解:但是现在,我知道为什么要源文件包含自己的头文件了。
如下,一段书中的原话:“如果希望让编译器检查声明的一致性, 一定要把全局声明放到头文件中。
特别是, 永远不要把外部函数的原型(也就是函数声明)放到.c 文件中: 通常它与定义的一致性不能得到检查, 而矛盾的原型(也就是函数声明)比不用还糟糕。
”注意:外部函数的原型,就是外部函数的声明。
对这段话的理解:为什么:“永远不要把外部函数的原型放到.c 文件中”这个外部函数A指的是B.c文件之外定义的函数,B.c文件中需要使用外部函数A,就需要先对外部函数A声明(对外部函数的声明就是外部函数原型)。
对这个外部函数A的声明,不能放在B.c文件里面来实现。
以实例说明:①假若工程中有2个源文件a.c和b.c;a.c的头文件为a.h,b.c的头文件为b.h。
②a.c中定义了一个函数sum。
③b.c要引用sum 这个函数。
做法是:在b.c中声明sum这个函数。
然后b.c就可以使用sum函数了。
这样的做法就是把外部函数sum的声明放到了b.c中来。
然而,这样的做法很不妥。
不妥的原因:sum是在a.c中定义的,而声明确是在b.c中,sum函数的定义和声明不是在同一个文件中的。
定义和声明不在同一个文件中,编译的时候,编译器就不能对定义和声明的一致性进行检查。
这样,如果sum的定义和声明不一致,编译器就无法检查出来(定义和声明不在同一个文件中),那么编译的时候不会报错,但是程序运行的时候就可能会出错。
头文件(.h)和源文件(.cpp)的区别(c++)
头⽂件(.h)和源⽂件(.cpp)的区别(c++)头⽂件(.h)头⽂件⽤来写类的声明(包括类的成员的声明和⽅法声明)、函数原型、#define 常数等,但是很少会写出具体的实现和细节。
就好⽐抽象类⼀样。
头⽂件很有意思的是,开头和结尾必须按照以下格式:#ifndef MYCLASS_H#define MYCLASS_H// code here#endif当时我看到这个是极其的不理解和迷茫的,后来阅读了别⼈的博⽂才略懂。
⾸先解释他是⼲嘛使的,这是防⽌头⽂件被重复引⽤。
什么叫被重复引⽤?就是同⼀个头⽂件(.h)在同⼀个源⽂件(.cpp)中被include了多次。
这种错误常常是因为include嵌套。
举个最简单的例⼦,存在cellphone.h这个头⽂件引⽤了#include "huawei.h",之后⼜有china.cpp这个源⽂件同时导⼊了#include "cellphone.h" 和 #include "huawei.h"。
此时huawei.h就在⼀个源⽂件⾥引⽤了两次。
那么,某些时候,只是因为include了两遍,增⼤了编译器的⼯作量。
如果是⼩型程序的话还好说,但是⼤型⼯程甚⾄会增长⼏个⼩时的编译时间。
但是另⼀些情况,会引起很严重的错误。
⽐如在头⽂件中定义了全局变量会引起重复定义。
所以就有了我们上⾯那些看起来乱七⼋糟的代码,下⾯开始解释。
#ifndef MYCLASS_H 的意思是 if not define myclass.h,这样看就很好理解了,如果引⽤这个头⽂件的源⽂件不存在myclass.h这个头⽂件,那么接下⾏ #define MYCALSS_H,引⼊myclass.h。
然后就是我们头⽂件的代码。
如果已经有了,直接跳到 #endif。
理论上来说,上⾯这个⽚段的MYCLASS_H是可以任意命名的,但是约定俗成的,为了可读性的,我们都把它命名为这个头⽂件的⼤写和下划线的形式。
源文件与头文件
14.1源文件和头文件和别的一些语言不同,C,C++的代码文件有“头文件”和“代码文件”之分。
二者合起来我们称为单元(Unit)文件。
扩展名为 .c 或 .cpp 的文件,主要用以实现程序的各种功能,我们称为代码文件。
扩展名为 .h 的文件,称为头文件。
在头文件里主要写一些函数、数据(包括数据类型的定义)、等的声明,这样可以在多个.c或.cpp文件内共享这些函数、数据。
说它可以起到函数“名片夹”的作用。
大都数时候,源文件和头文件是对应出现的,比如有一个A.cpp 的源文件,就会有一个A.h 的头文件。
这种情况在我们写应用程序时,更是常见。
所以C++ Builder对此进行了强化。
比如,它支持在同名源文件和头文件之间通过热键来回切换。
在CB6.0里,编辑器打开对应的源文件和头文件时,将显示为同一页下的两个子页。
找到“代码窗口”。
如果你看到的是一个叫"Form1"的表单,请按F12,“代码窗口”将跑到前面。
它的标题应该是默认的"Unit1.cpp"。
正是当前代码文件的文件名。
如下图:对于CB6,还可以看到在该窗口的底部有这样一个分页:源文件:Unit1.cpp 和头文件:Unit1.h 并列着,我们可以方便地选择。
至于"Diagram",称为“图解”。
这是一个给这个源文件加配套注解,及表单上各控件的依赖关系的地方。
如果是一个开发小组在进行共同开发,严格地要求每个成员为每个单元文件写上“Diagram”,可以更好地实现程序员之间的沟通。
CB5没有这些,不过下面的热键操作两个版本均一样的,要求大家记住。
按Ctrl + F6 可以在源文件和头文件之间来回切换。
请大家试试。
这个简单的操作将在我们今后的编程过程中高频率地使用。
14.2如何创建多个单元文件前面我们在“Wind ows应用程序工程”中看到了头文件与源文件的匹配关系,在“控制台”的工程中,也同样存在。
C语言中,头文件和源文件的关系
C语言中,头文件和源文件的关系(转)简单的说其实要理解C文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程:1.预处理阶段2.词法与语法分析阶段3.编译阶段,首先编译成纯汇编语句,再将之汇编成跟CPU相关的二进制码,生成各个目标文件(.obj文件)4.连接阶段,将各个目标文件中的各段代码进行绝对地址定位,生成跟特定平台相关的可执行文件,当然,最后还可以用objcopy生成纯二进制码,也就是去掉了文件格式信息。
(生成.exe文件)编译器在编译时是以C文件为单位进行的,也就是说如果你的项目中一个C文件都没有,那么你的项目将无法编译,连接器是以目标文件为单位,它将一个或多个目标文件进行函数与变量的重定位,生成最终的可执行文件,在PC上的程序开发,一般都有一个main函数,这是各个编译器的约定,当然,你如果自己写连接器脚本的话,可以不用main函数作为程序入口!!!!(main .c文件目标文件可执行文件)有了这些基础知识,再言归正传,为了生成一个最终的可执行文件,就需要一些目标文件,也就是需要C文件,而这些C文件中又需要一个main 函数作为可执行程序的入口,那么我们就从一个C文件入手,假定这个C文件内容如下:#include#include "mytest.h"int main(int argc,char **argv){test = 25;printf("test.................%d/n",test);}头文件内容如下:int test;现在以这个例子来讲解编译器的工作:1.预处理阶段:编译器以C文件作为一个单元,首先读这个C文件,发现第一句与第二句是包含一个头文件,就会在所有搜索路径中寻找这两个文件,找到之后,就会将相应头文件中再去处理宏,变量,函数声明,嵌套的头文件包含等,检测依赖关系,进行宏替换,看是否有重复定义与声明的情况发生,最后将那些文件中所有的东东全部扫描进这个当前的C文件中,形成一个中间“C文件”2.编译阶段,在上一步中相当于将那个头文件中的test变量扫描进了一个中间C文件,那么test变量就变成了这个文件中的一个全局变量,此时就将所有这个中间C文件的所有变量,函数分配空间,将各个函数编译成二进制码,按照特定目标文件格式生成目标文件,在这种格式的目标文件中进行各个全局变量,函数的符号描述,将这些二进制码按照一定的标准组织成一个目标文件3.连接阶段,将上一步成生的各个目标文件,根据一些参数,连接生成最终的可执行文件,主要的工作就是重定位各个目标文件的函数,变量等,相当于将个目标文件中的二进制码按一定的规范合到一个文件中再回到C文件与头文件各写什么内容的话题上:理论上来说C文件与头文件里的内容,只要是C语言所支持的,无论写什么都可以的,比如你在头文件中写函数体,只要在任何一个C文件包含此头文件就可以将这个函数编译成目标文件的一部分(编译是以C文件为单位的,如果不在任何C文件中包含此头文件的话,这段代码就形同虚设),你可以在C文件中进行函数声明,变量声明,结构体声明,这也不成问题!!!那为何一定要分成头文件与C文件呢?又为何一般都在头件中进行函数,变量声明,宏声明,结构体声明呢?而在C文件中去进行变量定义,函数实现呢??原因如下:1.如果在头文件中实现一个函数体,那么如果在多个C文件中引用它,而且又同时编译多个C文件,将其生成的目标文件连接成一个可执行文件,在每个引用此头文件的C文件所生成的目标文件中,都有一份这个函数的代码,如果这段函数又没有定义成局部函数,那么在连接时,就会发现多个相同的函数,就会报错2.如果在头文件中定义全局变量,并且将此全局变量赋初值,那么在多个引用此头文件的C文件中同样存在相同变量名的拷贝,关键是此变量被赋了初值,所以编译器就会将此变量放入DATA段,最终在连接阶段,会在DATA段中存在多个相同的变量,它无法将这些变量统一成一个变量,也就是仅为此变量分配一个空间,而不是多份空间,假定这个变量在头文件没有赋初值,编译器就会将之放入BSS段,连接器会对BSS段的多个同名变量仅分配一个存储空间3.如果在C文件中声明宏,结构体,函数等,那么我要在另一个C文件中引用相应的宏,结构体,就必须再做一次重复的工作,如果我改了一个C文件中的一个声明,那么又忘了改其它C文件中的声明,这不就出了大问题了,程序的逻辑就变成了你不可想象的了,如果把这些公共的东东放在一个头文件中,想用它的C文件就只需要引用一个就OK了!!!这样岂不方便,要改某个声明的时候,只需要动一下头文件就行了4.在头文件中声明结构体,函数等,当你需要将你的代码封装成一个库,让别人来用你的代码,你又不想公布源码,那么人家如何利用你的库呢?也就是如何利用你的库中的各个函数呢??一种方法是公布源码,别人想怎么用就怎么用,另一种是提供头文件,别人从头文件中看你的函数原型,这样人家才知道如何调用你写的函数,就如同你调用printf函数一样,里面的参数是怎样的??你是怎么知道的??还不是看人家的头文件中的相关声明啊!!!当然这些东东都成了C标准,就算不看人家的头文件,你一样可以知道怎么使用c语言中.c和.h文件的困惑本质上没有任何区别。
头文件和源文件的用法
头文件和源文件的用法头文件和源文件是C++程序中最基本的组成部分之一。
头文件通常包含函数和变量的声明,而源文件包含函数的实现和变量的定义。
程序员使用头文件和源文件来帮助组织和分离程序代码,以增强代码的可读性和可维护性。
一、头文件的作用头文件是C++程序中的重要组成部分,其作用有以下三个方面:1. 声明函数和变量头文件可以包含函数和变量的声明,这些声明通常是在源文件中使用的函数和变量的可见性,也就是让源文件知道这些内容,以便正确地使用它们。
头文件声明的内容可以在多个源文件中使用,因此它们通常是共享的。
2. 定义常量头文件中定义的常量是程序中不变的数值,例如常量π等,使用者可以在程序中引用这些常量名而无需在源文件中再次定义。
3. 包含其他头文件头文件还可以包含其他头文件,这些头文件包含了其他的函数和变量,它们都可以在程序中使用。
在程序的头文件层次结构中,通常会让一个主要的头文件包含所有的子头文件,以减少重复代码并保证程序的清晰易读性。
二、源文件的用途源文件通常包含函数的实现和变量的定义。
这些文件是程序的核心部分,用于执行代码并计算结果。
源文件的主要用途有以下几个方面:1. 实现函数源文件中的函数实现了程序设计中的算法和逻辑。
这些实现是由程序员编写的,根据参数的值计算结果并返回。
这样源文件中的函数可以在程序中被调用和使用。
2. 定义变量源文件中定义变量是为了处理数据时的存储和管理。
变量是在程序中被赋值和使用的,因此源文件中的变量是由程序员定义的。
3. 包含头文件源文件通常包含头文件,以便知晓头文件中声明的函数和变量,同时避免重复定义。
三、头文件和源文件的区别头文件和源文件有以下几个区别:1. 头文件提供了函数和变量的声明,而源文件提供了函数和变量的实现。
2. 头文件可以被多个源文件包含,以便共享声明,而源文件只能被单独编译为二进制代码。
3. 头文件通常使用.h扩展名,而源文件通常使用.cpp扩展名。
4. 头文件和源文件都是文本文件,但在处理方法上有不同的方式。
头文件和源文件
int number1 = f1(); int number2 = f2(number1); } /* end of main.cpp */ 这 样,便是一个完整的程序了。需要注意的是,.h 文件不用写在编译器的命令之后,但它必须要在编译器 找得到的地方(比如跟 main.cpp 在一个目录下)。 main.cpp 和 math.cpp 都可以分别通过编译,生成 main.o 和 math.o,然后再把这两个目标文件进行链接,程序就可以运行了。 三、#include #include 是一个来自 C 语言的宏命令,它在编译器进行编译之前,即在预编译的时候就会起作用。 #include 的作用是把它后面所写的那个文件的内容,提的是,它本身是没有其它任何作用与副功能的,它的作用就是把每一个它出现的地方,替换成它后 面所写的那个文件的 内容。简单的文本替换,别无其他。因此,main.cpp 文件中的第一句(#include "math.h"),在编译之前就会被替换成 math.h 文件的内容。即在编译过程将要开始的时候,main.cpp 的 内容已经发生了改变:
所以,应该记住的一点就是,.h 头文件中,只能存在变量或者函数的声明, 而不要放定义。即,只能在头 文件中写形如:extern int a;和 void f();的句子。这些才是声明。如果写上 int a;或者 void f() {}这样 的句子,那么一旦这个头文件被两个或两个以上的.cpp 文件包含的话,编译器会立马报错。(关于 extern, 前面有讨论过,这里不再讨论定义跟 声明的区别了。) 但是,这个规则是有三个例外的。 一,头文件中可以写 const 对象的定义。因为全局的 const 对象默 认是没有 extern 的声明的,所以它只 在当前文件中有效。把这样的对象写进头文件中,即使它被包含到其他多个.cpp 文件中,这个对象也都只 在包含它的 那个文件中有效,对其他文件来说是不可见的,所以便不会导致多重定义。同时,因为这些.cpp 文件中的该对象都是从一个头文件中包含进去的,这样也就保证 了这些.cpp 文件中的这个 const 对象的 值是相同的,可谓一举两得。同理,static 对象的定义也可以放进头文件。 二,头文件中可 以写内联函数(inline)的定义。因为 inline 函数是需要编译器在遇到它的地方根据它的 定义把它内联展开的,而并非是普通函数那样可以先声明再链 接的(内联函数不会链接),所以编译器就需 要在编译时看到内联函数的完整定义才行。如果内联函数像普通函数一样只能定义一次的话,这事儿就难 办了。因为在 一个文件中还好,我可以把内联函数的定义写在最开始,这样可以保证后面使用的时候都可 以见到定义;但是,如果我在其他的文件中还使用到了这个函数那怎么办 呢?这几乎没什么太好的解决办 法,因此 C++规定,内联函数可以在程序中定义多次,只要内联函数在一个.cpp 文件中只出现一次,并且 在所有的.cpp 文 件中,这个内联函数的定义是一样的,就能通过编译。那么显然,把内联函数的定义放进 一个头文件中是非常明智的做法。 三,头文件中可以写类 (class)的定义。因为在程序中创建一个类的对象时,编译器只有在这个类的定义 完全可见的情况下,才能知道这个类的对象应该如何布局,所以,关于类的 定义的要求,跟内联函数是基 本一样的。所以把类的定义放进头文件,在使用到这个类的.cpp 文件中去包含这个头文件,是一个很好的 做法。在这里,值得一提 的是,类的定义中包含着数据成员和函数成员。数据成员是要等到具体的对象被 创建时才会被定义(分配空间),但函数成员却是需要在一开始就被定义的,这也就 是我们通常所说的类的 实现。一般,我们的做法是,把类的定义放在头文件中,而把函数成员的实现代码放在一个.cpp 文件中。 这是可以的,也是很好的办法。 不过,还有另一种办法。那就是直接把函数成员的实现代码也写进类定义 里面。在 C++的类中,如果函数成员在类的定义体中被定义,那么编译器会视这个函数为 内联的。因此, 把函数成员的定义写进类定义体,一起放进头文件中,是合法的。注意一下,如果把函数成员的定义写在类 定义的头文件中,而没有写进类定义中, 这是不合法的,因为这个函数成员此时就不是内联的了。一旦头 文件被两个或两个以上的.cpp 文件包含,这个函数成员就被重定义了。 五、头文件中的保护措施 考 虑一下,如果头文件中只包含声明语句的话,它被同一个.cpp 文件包含再多次都没问题——因为声明语 句的出现是不受限制的。然而,上面讨论到的头文件中的 三个例外也是头文件很常用的一个用处。那么, 一旦一个头文件中出现了上面三个例外中的任何一个,它再被一个.cpp 包含多次的话,问题就大了。因为 这三个 例外中的语法元素虽然“可以定义在多个源文件中”,但是“在一个源文件中只能出现一次”。设想一 下,如果 a.h 中含有类 A 的定义,b.h 中含有类 B 的定 义,由于类 B 的定义依赖了类 A,所以 b.h 中也 #include 了 a.h。现在有一个源文件,它同时用到了类 A 和类 B,于是程序员在这个源文件中既把 a.h 包 含进来了,也把 b.h 包含进来了。这时,问题就来了:类 A 的定义在这个源文件中出现了两次!于是整个 程序就不能通过编译了。你也许会认为这是程序 员的失误——他应该知道 b.h 包含了 a.h——但事实上他 不应该知道。 使用"#define"配合条件编译可以很好地解决这个问题。在一 个头文件中,通过#define 定义一个名字, 并且通过条件编译#ifndef...#endif 使得编译器可以根据这个名字是否被定义,再决定要不要继 续编译该 头文中后续的内容。这个方法虽然简单,但是写头文件时一定记得写进去。
头文件与源文件
头⽂件与源⽂件以下内容全部是个⼈总结,如果有错误请指正!在初学C++的时候,我总是彷徨于不恰当使⽤头⽂件、声明、定义、源⽂件⽽导致的各种Link错误。
今天我想通过⼀些简单的测试,得到⼀些概括的,⼀般性的结论。
(因为我没有学习过C++的编译器,所以我所以这些结论仅仅是⼀些根据⼀些现象的猜想)实验环境:集成开发环境(Visual Studio 2017),并没有直接使⽤过g++的shell命令。
1. 在Visual Studio 2017 环境下,项⽬中的⽂件会被赋予不同的类型每个类型有各⾃的作⽤。
1.1 头⽂件 头⽂件不会参与Link过程,所以头⽂件中如果存在语法错误,是不会被发现的。
e.g:在头⽂件⽂件夹下新建⽂件:输⼊“wrong code”。
运⾏源.cpp,发现正常运⾏。
1.2 源⽂件 源⽂件既可以被 #include 导⼊(因为头⽂件源⽂件类型的区分是VS的限定,但对于⼀个编译器来说任何⽂件都是没有区别的,只有输⼊的参数不同),但导⼊之后同样会编译⾃⾝之后Link。
e.g:在源⽂件⽂件夹下新建⽂件a.h: 定义函数 a, 在源.cpp 中include该⽂件,并使⽤a函数。
系统将出现符号重定义的错误:因为 VS认为a.h是源⽂件故编译了他。
⽽源.cpp中include的部分也被编译,故出现了两个相同的符号。
2. C++ 的 #include 对⽂件的名字(后缀)不敏感2.1 可以使⽤⽆ .h 后缀的⽂件作为 #include 的⽬标 这个是⼀个不需要实验的结论,因为常⽤的#include <iostream>便是⼀个很好的例⼦。
2.2 如果把⼀个正常的 .h ⽂件改成 .cpp 后缀,效果是⼀样的正常情况:// 源.cpp#include "A"#include <iostream>int main() {A a;//int main2();std::cout << "在main⾥:" << a.value << std::endl;//std::cout << "在main⾥:" << a.value << " " << a.functionA() << std::endl;//std::cout << "在main2⾥:";//main2();getwchar();return1;}// A#pragma onceclass A{public:int value = 100;//A();//~A();int functionA();private:};正常输出当 #include 的对象改为 .cpp 后缀// 源.cpp#include "A.cpp"#include <iostream>int main() {A a;//int main2();std::cout << "在main⾥:" << a.value << std::endl;//std::cout << "在main⾥:" << a.value << " " << a.functionA() << std::endl;//std::cout << "在main2⾥:";//main2();getwchar();return1;}// A.cpp#pragma onceclass A{public:int value = 100;//A();//~A();int functionA();private:};正常输出3. #include 是的本质是将 #include⽂件的全体代码替换到当前位置3.1 (猜想)因为 #include 带 “#” 所以是预处理过程,其过程将先与其他语法检查步骤使⽤ #include 语句// 源.cpp#include "testInclude.h"#include <iostream>int main() {std::cout << functionToTestInclude();getwchar();return1;}// testInclude.h#pragma onceint functionToTestInclude() { return1; }正常输出直接将 #include 对象替换掉 #include 语句// 源.cppint functionToTestInclude() { return1; }#include <iostream>int main() {std::cout << functionToTestInclude();getwchar();return1;}正常输出结论:1. 替换后效果⼀样。
C++中源文件和头文件的区别
C++的源代码文件分为两类:头文件(Header file)和源文件(Source code file)。
头文件用于存放对类型定义、函数声明、全局变量声明等实体的声明,作为对外接口;而源程序文件存放类型的实现、函数体、全局变量定义.C++的源代码文件分为两类:头文件(Header file)和源文件(Source code file)。
头文件用于存放对类型定义、函数声明、全局变量声明等实体的声明,作为对外接口;而源程序文件存放类型的实现、函数体、全局变量定义。
对于商业C++程序库,一般把头文件随二进制的库文件发布,而源代码保留。
一般情况下头文件常以.h或.hpp作为扩展名,而实现文件常以.cpp或.cc为扩展名。
头文件一般不直接编译,一个源文件代表一个“编译单元”。
在在编译一个源文件时,如果引用的类型、函数或其它实体不在本编译单元内,可以通过引用头文件将其它编译单元内实现的实体引入到本编译单元。
而从本质上讲,这些源代码文件都是纯文本文件,可以使用任何一款文本编译器进行源代码的编辑,并没有本质的区别,这些头文与实现文件的扩展名只是一种习惯。
而C++的标准库的头文件则不使用扩展名,例如string、 iostream、cstdio 等头文件。
对与源文件也一样,你完全可以使用.inl或.cplusplus作为文件的扩展名。
事实上,在一些C++的项目中.inl被用作源代码文件的扩展名,保存内联函数,直接包含在源文件中,如ACE(the Adaptive Communication Environment, /~schmidt/ACE.html)等。
gcc默认支持的C++源文件扩展名有.cc、.cp、.cpp、.cxx、.c++、.CPP、.C(注意后两项是大写,在Unix/Linux 上的文件名是区分大小写的)。
例如在gcc中你可以这样编译一个扩展名为.cplusplus的C++程序:g++ -x c++ demo.cplusplus虽然文件名对程序没有任何影响,但.cpp和.cc这些扩展名是编译器默认支持的,使用这些扩展名您就不需要手动添加编译选项支持您使用的扩展名,如gcc 中的-x选项。
C语言中为什么要头文件和源文件分开写
C语言中为什么要头文件和源文件分开写对c&c++程序来说,基本上来说都是要把源文件和头文件分别编写。
一般都是代表一个基本功能的源文件引用相应的头文件。
一个相关功能的模块可能有若干对源文件和头文件组成。
这是基于组件编程的核心。
在我看来,他的好处是巨大的,是java不可比拟的,也是不可复制的:c语言中头文件中一般定义了函数的声明、结构体的定义、宏定义。
(常量和全局变量最好放到源文件中)1) 从业务扩展性上看:头文件中放函数的声明,函数由源文件实现,这就是将面向接口编程:接口和实现分开,这在面对业务变更频繁的需求中技术实现的好处是显而易见的--只要定义出良好地、扩展性高的接口,实现是可以很方便的更换。
2) 从程序架构上看:代码在在大型程序中需要分成不同的模块,单一模块中又可能分为不同的业务功能单元,他们间有很多相互的调用。
头文件中的方法声明、结构体定义、宏就都可以充当这部分的模块与模块间、业务功能单位间的接口调用。
模块与模块间,功能单元与功能单元间都是面向接口的调用,耦合性低,这正是基于组件编程的核心思想。
3) 从某些技术角度实现上看:头文件可通过宏定义来保证类定义、结构体定义、宏定义的唯一性。
确实很方便,不容易出错。
在用makefile编译程序时,各个功能单元单独编译,构成中间文件.最终这些中间文件链接成可执行程序,在这些中间文件中重复引用同一头文件是不可避免的。
但如果头文件有保护性编程就可以很容易保证类、结构体、宏定义的唯一性----最终链接成可执行程序时,可执行程序中代码区中只会有唯一的类、结构体、宏的定义,其他都因为不满足唯一性保护失效。
这样的实现是不是很‘优雅’:)))? 我不知道你是否是这样认为,至于我,我是这样认为,haha~。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
c语言笔记--头文件和源文件的区别
关于头文件和源文件的分别
首先,我们可以将所有东西都放在一个.cpp文件内.
然后编译器就将这个.cpp编译成.obj,obj是什么东西?
就是编译单元了.一个程序,可以由一个编译单元组成,
也可以有多个编译单元组成. 如果你不想让你的源代码变得很难阅读的话,
就请使用多个编译单元吧.(一个函数不能放到两个编译单元里面,但两个以上
就可以分别放在一个单元,也就是cpp里面)
那么就是一个.cpp对应一个.obj,然后将所有的obj链接起来(通过一个叫链接器的程序),
组成一个.exe,也就是程序了.
如果一个.cpp要用到另一个.cpp定义的函数怎么办? 只需在这个.cpp种写上他的函数声明就可以了.其余工作由链接器帮你完成,你可以随便调用该函数.
链接器将所有的obj连接起来,但是如果碰巧有相同的函数或外部变量怎么办?他如何识别? 一般来说是不能允许在同一个程序中,出现两个一样的函数名或外部变量名.
但是只得庆幸的是,c++可以通过一种叫做链接属性的关键字来限定,你这个函数是属于整个程序
公用的,还是只是在一个编译单元obj里面使用的.
这些关键字就是extern 和static;extern是外部链接的意思,也就是除了这个单元,外部的单元
也是能够访问这个函数的.static 是内部链接,自属于自己单元.
说了这么久,还没有说.h的作用呢?
其实没有.h也能很好的工作,但是当你发现一个外部链接的函数或外部变量,需要许多份
声明,因为c++这种语言,在使用函数和变量的时候,必须将他声明,为何要声明?声明之后才
知道他的规格,才能更好的发现不和规格的部分.你别妄想一个编译单元,会自动从另一个
编译单元那里得到什么信息,知道你是如何定义这个函数的.
所以说,只要使用到该函数的单元,就必须写一份声明在那个.cpp里面,这样是不是很麻烦,
而且,如果要修改,就必须一个一个修改.这真让人受不了.
.h就是为了解决这个问题而诞生,他包含了这些公共的东西.然后所有需要使用该函数的.cpp,只需要
用#include包含进去便可.以后需要修改,也只是修改一份内容.
请注意不要滥用.h,.h里面不要写代码,.h不是.cpp的仓库,什么都塞到里面.
如果在里面写代码,当其他.cpp包含他的时候,就会出现重复定义的情况,
比如将函数func(){printf};放到头文件a.h,里面还有一些a.cpp需要的声明等;
然后你发现b.cpp需要用到a.cpp里面的一个函数,就很高兴的将a.h包含进来.
注意,#include并不是什么申请指令,他就是将指定的文件的内容,原封不动的拷贝进来.
这时候实际上a.cpp和b.cpp都有一个func()函数的定义.
如果这个函数是内部链接static的话,还好,浪费了一倍空间;
如果是extern,外部链接(这个是默认情况),那么根据在同一个程序内不可出现
同名函数的要求,连接器会毫不留情给你一个连接错误!
;。