举个简单的例子!你会容易理解的。
你写一个stack.h的头文件,里面声明几个函数原形:
stack.h
#ifndef STACK_H
#define STACK_H
extern void push(char);
extern char pop(void);
extern int is_empty(void);
#endif
你可以在其相应的stack.c中对这些函数进行实现
stack.c
#include "stack.h"
void push(char)
{
/*your code*/
}
char pop(void)
{
/*your code*/
}
int is_empty(void)
{
/*your code*/
}
在main.c中你可以这样写
#include <stdio.h>
#include "stack.h"
int main()
{
push('a');
push('b');
push('c');
while(!is_empty())
putchar(pop());
putchar('\n');
return 0;
}
大致格式就是这样,在stack.h(这个名字你可以随便定),在这个头文件中声明函数原形,在相应的stack.c中进行函数定义与实现,主程序文件中包含这个头文件之后就可以调用stack.h中声明的函数,编译后无错误即可执行,
在我们语言的初学阶段,往往我们的程序只有一个.c的文件或这很少的几个,这时我们就很少遇到头文件组织这个头疼的问题,随着我们程序的增加,代码 量到了几千行甚至几万行,文件数也越来越多。这时这些文件的组织就成了一个问题,其实说白了这些文件的组织问题从理论上来说是软件工程中的模块设计等等的问题。
由上可以看出,.h文件最初就是用来给变量和函数提供一些全局性的声明,这些声明被其他.c文件共享,方便变量和声明的修改,使得大型代码逻辑更清晰更易于维护。因此.h文件中一般是声明,很少有代码的具体实现。那么为什么在.h文件中实现函数也不会出错呢?在.h文件中实现函数与在.c文件中实现函数有什么区别和联系呢?普通的.C文件和包含main函数的c文件有什么区别和联系呢?
要解决上述问题,首先必须弄清编译器的工作原理。编译器的最终目的是将程序员编写的源代码转换成机器能够识别运行的二进制机器码。大体上分,可以分为4个步骤:
1.头文件的预编译,预处理
编译器在编译源代码时,会先编译头文件,保证每个头文件只被编译一次。
在预处理阶段,编译器将c文件中引用的头文件中的内容全部写到c文件中。
2.词法和语法分析(查错)
3.编译(汇编代码)
转化为汇编码,这种文件称为目标文件。
4.链接(二进制机器码)
将汇编代码转换为机器码,生成可执行文件。
在编译过程中,.h文件中的所有内容会被写到包含它的.c文件中,而所有的.c文件以一个共同的main函数作为可执行程序的入口。因此,在.h文件中编写函数实现并不会出错,相当于所有.h的内容最后都被写到了main.c文件中。但是为了逻辑性、易于维护性以及一些其他目的(可参考c语言中.h文件和.c文件的解析 ),一般在.h文件中写函数的声明,在.c文件中编写函数的实现。
预编译
伪指令特殊符号处理
编译
默认得到二进制 ,但是按步骤是得到汇编代码。
汇编
把汇编代码翻译成机器码 。通常把编译和汇编合成一个编译 ,将经过预处理后的源文件通过编译得到机器码 ,目标文件
链接
将多个库文件,目标文件对接 ,生成可执行文件
声明:本文中如出现侵权文字,请留言