您的位置 首页 主动

头文件与之完成文件的的联系

你理解简单的含义吗?关于两者以前的关系,要从N年以前说起了~longlongago,onceauponatime那是一个被遗忘的年代,在编译

你了解简略的含义吗?

关于两者曾经的联络,要从N年曾经说起了~ long long ago,once aupon a time …….
那是一个被忘记的时代,在编译器只知道.c(.cpp))文件,而不知道.h是何物的时代。
那时的人们写了许多的.c(.cpp)文件,渐渐地,人们发现在许多.c(.cpp)文件中的声明句子便是相同的,但他们却不得不一个字一个字地重复地将这些内容敲入每个.c(.cpp)文件。但更为恐惧的是,当其间一个声明有改变时,就需求查看一切的.c(.cpp)文件,并修正其间的声明,啊~简直是国际末日来临!
总算,有人(或许是一些人)再不能忍耐这样的摧残,他(们)将重复的部分提取出来,放在一个新文件里,然后在需求的.c(.cpp)文件中敲入#includeXXXX这样的句子。这样即便某个声明产生了改变,也再不需求处处寻觅与修正了—国际仍是那么夸姣!
因为这个新文件,常常被放在.c(.cpp)文件的头部,所以就给它起名叫做“头文件”,扩展名是.h.
从此,编译器(其实是预处理器)就知道世上除了.c(.cpp)文件,还有个.h的文件,以及一个叫做#include指令。

尽管后来又产生许多的改变,可是这样的用法一向连续至今,仅仅时日久远了,人们便淡忘了当年的缘由算了。
提到了头文件,就说说它的效果吧~
想到了林锐GG写的高质量C/C++编程上头文件的效果的简略描绘:
(1)通过头文件来调用库功用。在许多场合,源代码不便利(或禁绝)向用户发布,只需向用户供给头文件和二进制的库即可。用户只需求依照头文件中的接口声明来调用库功用,而不必关怀接口怎样完成的。编译器会从库中提取相应的代码。
(2)头文件能加强类型安全查看。假如某个接口被完成或被运用时,其方法与头文件中的声明不一致,编译器就会指出过错,这一简略的规则能大大减轻程序员调试、改错的担负。

预处理是编译器的前驱,效果是把存储在不同文件里的程序模块集成为一个完好的源程序.
#include自身仅仅一个简略的文件包括预处理指令,即为把include的后边文件放到这条指令这儿,除此之外,没有其它的用途(至少我也样以为).

我对天地一笑兄的观念,十分附和,根底的东东一定要弄了解.
我下面就天地一笑兄的比方做讲,齐备他的一些让人疑惑不解的时分~

比方:
//a.h
void foo();

//a.c
#include “a.h”//我的问题出来了:这句话是要,仍是不要?
void foo()
{
return;
}

//main.c
#include “a.h”
int main(int argc, char *argv[])
{
foo();
 return 0;
}

针对上面的代码,请答复三个问题:
a.c中的#include “a.h”这句话是不是剩余的?
1.为什么常常见xx.c里边include对应的xx.h?
2.假如a.c中不写,那么编译器是不是会主动把.h文件里边的东西跟同名的.c文件绑定在一起?
3.第三个问题我给他改了一下:假如a.c中不写include<>,那么编译器是不是会主动把.h文件里边的东西跟同名的.c文件绑定在一起?

下面是天地一笑的原话:

从C编译器视点看,.h和.c皆是浮云,便是改名为.txt、.doc也没有大的别离。换句话说,便是.h和.c没啥必定联络。.h中一般放的是同名.c文件中界说的变量、数组、函数的声明,需求让.c外部运用的声明。这个声明有啥用?仅仅让需求用这些声明的当地便利引证。因为#include “xx.h”这个宏其实践意思便是把当时这一行删掉,把xx.h中的内容原封不动的刺进在当时行的方位。因为想写这些函数声明的当地十分多(每一个调用xx.c中函数的当地,都要在运用前声明一会儿),所以用#include “xx.h”这个宏就简化了许多行代码——让预处理器自己替换好了。也便是说,xx.h其实仅仅让需求写xx.c中函数声明的当地调用(能够少写几行字),至于include这个.h文件是谁,是.h仍是.c,仍是与这个.h同名的.c,都没有任何必定联络。
这样你或许会说:啊?那我平常只想调用xx.c中的某个函数,却include了xx.h文件,岂不是宏替换后呈现了许多无用的声明?没错,的确引入了许多废物,可是它却省了你不少翰墨,并且整个版面也看起来清新的多。鱼与熊掌不行得兼,便是这个道理。横竖多些声明(.h一般只用来放声明,而放不界说,拜见拙著“过马路,左右看”)也无坏处,又不会影响编译,何乐而不为呢?
翻回头再看上面的3个问题,很好答复了吧?
它的答复如下:

答:1.不一定。这个比方中显然是剩余的。可是假如.c中的函数也需求调用同个.c中的其它函数,那么这个.c往往会include同名的.h,这样就不需求为声明和调用次序而忧愁了(C言语要求运用之前有必要声明,而include同名.h一般会放在.c的最初)。有许多工程乃至把这种写法约定为代码标准,以标准出明晰的代码来。
2.答:1中现已答复过了。
3.答:不会。问这个问题的人肯定是概念不清,要不便利是想混水摸鱼。十分厌烦的是我国的许多考试出的都是这种烂题,生怕他人有个清楚的概念了,肯定要把考生搞晕。

over!

在此里要清晰一点,编译器是依照编译单元进行编译的,所谓的编译单元,是指一个.c文件以及它所include的一切.h文件.最直观的了解便是一个文件,一个工程中能够包括许多文件,其间有一个程序的进口点,即咱们一般所说的main()函数(当然也能够没有这个函数,程序照样能发动,具体见我的blog中).在没有这个程序进口点的情况下,编译单元只生成方针文件object file(.o文件,windows下叫做.obj).

这个比方中一共包括了二个编译单元,别离是a.c,main.c,依照我所说的,在编译阶段仅仅生成各自的.o文件.这个阶段不好其它的文件产生任何的联络.
而include这个预处理指令产生在预处理阶段(新近编译阶段,仅仅编译器的一个前驱处理程序).

.h .c不见得是浮云,脱离了编译器谈这些没有任何的含义,抛开更深层次的这些,比方说,OS怎么发动这个文件,PE结构(linux下为elf)等等
编译器首先要辨认这个文件才或许去编译它,这是条件.假如你改了它的扩展名那么你的编译器还能知道它吗~上升到一个更高的层次上看待这个问题,XX兄说的也不错~我想XX兄说的意思便是两者不行因为姓名相同就以为两者有什么联络,姓名是能够随意的~
两者之间的联络,我在前面说过了,是因为前史的原因形成的,再加上人的习气,我想谁也不想多去记那么多文件名吧.(拿我举个比方,一个数
据表假如多于30个字段,我就觉得头大了,现在弄的表有的多达上百个字段,真希望那位高人研究出什么好的方法来~,也让咱们的国际夸姣一些~)

天地一笑的第三个问题很有代表性,屡次在网上看到,现在的编译器肯定没有那么智能,并且也没有有必要那么做.下面咱们首要聊聊编译器的处理进程.(我想初学者有疑问的正在于此,便是关于编译进程.h .c(.cpp)的改变不太了解,)

下面我说举个简略的比方来聊聊~
比方如下:
//a.h
classA
{
pubic:
intf(intt);
};

//a.cpp
#include“a.h”
intA::f(intt)
{
returnt;
}

//main.cpp
#include“a.h”
voidmain()
{
Aa;
a.f(3);
}
在预处理阶段,预处理器看到#include “文件名”就把这个文件读进来,比方它编译main.cpp,看到#include“a.h”,它就把a.h的内容读进来,它知道了,有一类A,包括一个成员函数f,这个函数承受一个int型的参数,回来一个int型的值。再往下编译很简单就把Aa这行读懂了,它知道是要拿A这个类在栈上生成一个方针。再往下,它知道了下面要调用A的成员函数f了,参数是3,因为它知道这个函数要一个整形数用参数,这个3正好匹配,那就正好把它放到栈上,生成一条调用f(int)函数的指令(一般或许是一句call),至于这个f(int)函数到底在哪里,它不知道,它藏着空,链接时再处理。它还知道f(int)函数要回来一个int,所以或许它也为这一点做好了预备(在比方中,咱们没用这个回来值,或许它就不处理)。再往下到文件结束了main.cpp编译好了,生成了main.obj。整个编译进程中底子就不需求知道a.cpp的内容。
同理,编译器再编译a.cpp,把f()函数编译好,编译a.cpp时,它也不必管其他,把f()编译好就行了。生成了a.obj。
终究一步便是链接的阶段了,链接器把项目中一切.cpp生成的一切.obj链接起来,
在这一步中,它就清晰了f(int)函数的完成地点的地址,把main.obj中空着的这个地址方位填上正确的地址。终究生成了可执行文件main.exe。

了解了吗?不了解那就多说几句了,咱们在学编译原理的时分都知道,编译器是分阶段进行的,每一个阶段将源程序从一种表明转换成另一种表明,一般情况下都进行如下次序:源程序->词法分器->语法分析器->语义分析器->中心代码生成器->代码优化器->代码生成器->方针程序.
其间这中心6项活动都要触及的两项首要活动是:符号管理器与过错处理器.
归根原因,这儿有一个叫做符号表的东东在里边让你着魔相同不了解,其实符号表是一个数据结构.编译器的根本一项功用便是要记载源程序中运用的标识符并搜集与每个标识符相关的各种特点信息.特点信息表明晰该标识符的存储方位/类型/效果域(在那个阶段有用)等信息,浅显的说一下便是,当编译器看到一个符号声明时,例如你的函数名它就会把它放到这个符号表中去挂号一下~符号表里存放着你的函数的进口地址,参数个数,回来信息等等一堆东西~而在联接阶段首要是处理工程中的符号表与调用对应处理联络,即咱们一般所说的解引证.
通过前面的,不知了解与否?

终究引证一下XXX兄的结束三点:
搞清楚语法和概念说易也易,说难也难。诀窍有三点:
1.不要晕着头作业,要抽暇多考虑考虑,多看看书;
2.看书要看好书,问人要问强者。烂书和烂人都会给你一个过错的概念,误导你;
3.熟能生巧是良训,一分辛苦一分才;

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/ziliao/zhudong/258764.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部