在C和C++中,首先要把源文件编译成中间代码文件,在windows下就是obj文件,linux下就是.o文件:object file。这个动作叫做编译,然后再把大量的object file合成执行文件。这个动作叫做链接(link)

编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在 C/C++文件中) ,只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对

应于一个中间目标文件(O 文件或是 OBJ 文件)

链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O 文件或是 OBJ 文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File) ,在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在 Windows 下这种包叫“库文件”(Library File),也就是 .lib 文件,在 UNIX 下,是 Archive File,也就是 .a 文件

所以整个过程大致分为2个步骤:

1 源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。 如果函数未被声明,编译器会给出一个警告,但可以生成 ObjectFile。

2而在链接程序时,链接器会在所有的 Object File 中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error) ,在 VC下,这种错误一般是:Link 2001 错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的 Object File.

下面我们就来看下makefile的语法规则。

makefile的语法规则如下

target:prerequisites

command

target也就是一个目标文件,可以是 Object File,也可以是执行文件。还可以是一个标签(Label)

prerequisites 就是,要生成那个 target 所需要的文件或是目标

command 也就是 make 需要执行的命令。 (任意的 Shell 命令)

说白了,其实就是整个工程里面的文件依赖关系,target 这一个或多个的目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command 中。说白一点就是说, prerequisites 中如果有一个以上的文件比 target 文件要新的话,command 所定义的命令就会被执行。这就是 Makefile 的规则。

一个工程中的源文件不计其数,按照不同的功能分类在若干的目录里面,makefile定义了一系列的规则,来制定那些文件需要先编译,那些文件后编译,那些文件重新编译。makefile最大的好处就是自动化编译。一旦写好,只需要一个make命令,整个过程都自动编译。极大提高开发的效率。我们先来看个简单的例子:
如果一个工程里面有1个头文件calc.h和2个C文件main.c,calc.c
main.c的内容如下:
#include "stdio.h"
#include "calc.h"
int main()
{
    int n,k;
    int c;
    n=3;
    k=4;
    c=calculate(n,k);
    printf("the value is %d\n",c);
}
calc.c的内容如下:
#include "calc.h"

int calculate(int n,int k)
{
    return n*k;
}
calc.h的内容如下:
#ifdef CALC_H
#define CALC_H
int calculate(int n,int k);
#endif
为了完成对工程文件案的编译,并生成执行文件main,按照如下的方式编译文件
root@zhf-linux:/home/zhf/zhf/c_prj/make_function# gcc main.c calc.c -o main
但是如果我对main.c做了修改。就需要把所有源文件都重新编译一遍,即使其他文件没有任何变化。也要跟着重新编译。一个大的软件项目上千个源文件组成,编译一次耗时很长。一个源文件修改导致全部重新编译肯定不合理。我们可以这样优化下:
gcc -c main.c
gcc -c calc.c
gcc main.o calc.o -o main
如果编译之后有对main.c做了修改,重新编译之需要两步:
gcc -c main.c
gcc main.o calc.o -o main
这样比之前的要省事一些了,但还是有问题,在calc.c和main.c都包含了calc.h。如果我对calc.h做了改动。所有包含calc.h的文件都得改动。而且还得到处去找那些包含了calc.h。还是很麻烦。比如在calc.h中增加了一个宏定义。并且在man.c和calc.c中都有用到这个变量。那么一旦calc.h修改了宏定义变量的值。calc.c和main.c都必须重新编译。
#define max_value 40

那么我们需要一种什么样的编译方式才能最省事呢:
1 如果这个工程没有被编译过,那么我们的所有C文件都要编译并被链接
2 如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并连接目标程序
3 如果这个工程的头文件被修改了,那么我们需要编译引用了这几个头文件的C文件并链接目标程序。
能达到上述目的的就是makefile文件了。在工程的文件路径下新建一个Makefile文件。其中内容如下:
main:main.o calc.o
    gcc -o main main.o calc.o
main.o:main.c calc.h
    gcc -c main.c
calc.o:calc.c calc.h
    gcc -c calc.c
clean:
    rm *.o
    rm main
执行make命令
root@zhf-linux:/home/zhf/zhf/c_prj/make_function# make
gcc -c main.c
gcc -c calc.c
gcc -o main main.o calc.o

来看下Makefile的规则:
1 第一条规则的目标为main。而为了得到main,必须先得到main.o calc.o这2个文件。所以make会进一步查找这2个条件为目标的规则。
2
第二条规则和第三套规则的目标三main.o和calc.o。main.o依赖于main.c和calc.h。为了得到main.o必行执行gcc
-c main. Calc.o依赖于calc.c和calc.h。为了得到calc.o必须执行gcc -c calc.c
3 最后的clean操作清除执行过程中产生的临时文件。当用make命令执行的时候,clean下的命令不会执行,要以make clean方式单独执行。执行后,所有*.o和main都被删除。
root@zhf-linux:/home/zhf/zhf/c_prj/make_function# ls -al
total 40
drwxr-xr-x 2 root root 4096 Nov 10 09:15 .
drwxr-xr-x 3 root root 4096 Nov  8 10:35 ..
-rw-r--r-- 1 root root  118 Nov 10 08:55 calc.c
-rw-r--r-- 1 root root   94 Nov 10 08:54 calc.h
-rw-r--r-- 1 root root 1056 Nov 10 09:15 calc.o
-rwxr-xr-x 1 root root 7396 Nov 10 09:15 main
-rw-r--r-- 1 root root  182 Nov 10 08:56 main.c
-rw-r--r-- 1 root root 1196 Nov 10 09:15 main.o
-rw-r--r-- 1 root root  142 Nov 10 09:15 Makefile
执行make clean
root@zhf-linux:/home/zhf/zhf/c_prj/make_function# make clean
rm *.o
rm main
root@zhf-linux:/home/zhf/zhf/c_prj/make_function# ls -al
total 24
drwxr-xr-x 2 root root 4096 Nov 10 09:29 .
drwxr-xr-x 3 root root 4096 Nov  8 10:35 ..
-rw-r--r-- 1 root root  118 Nov 10 08:55 calc.c
-rw-r--r-- 1 root root   94 Nov 10 08:54 calc.h
-rw-r--r-- 1 root root  182 Nov 10 08:56 main.c
-rw-r--r-- 1 root root  142 Nov 10 09:15 Makefile

下面我们来修改calc.h中的内容,#define max_value 50
看下编译内容。由于calc.c和main.c都包含了calc.h因此calc.c和main.c都会编译
root@zhf-linux:/home/zhf/zhf/c_prj/make_function# make
gcc -c main.c
gcc -c calc.c
gcc -o main main.o calc.o

如果只修改calc.c中的内容。calc.c修改如下
int calculate(int n,int k)
{
    printf("the value is %d",max_value);
    return n*k+n*k;
}
可以看到只编译了calc.c。main.c没有编译
root@zhf-linux:/home/zhf/zhf/c_prj/make_function# make
gcc -c calc.c
gcc -o main main.o calc.o

当没有任何文件修改的时候:会提示main is up to date
root@zhf-linux:/home/zhf/zhf/c_prj/make_function# make
make: 'main' is up to date.

那么make是如何工作的呢:
1 make会在当前目录下查找名为makefile或者Makefile的文件
2 如果找到,它会找文件中的第一个目标文件,在上面的例子中,它会找到main这个文件
3 如果main不存在,或者main所依赖的后面的.o文件的修改时间比main晚,那么就会执行后面所定义的命令来生成main这个文件
4 如果main所依赖的.o文件存在,那么make会在当前文件中查找目标为.o文件的依赖性,如果找到,则再根据那个规则生成.o文件
5 当C文件和H文件存在时,make会生成.o文件。然后再用.o文件生成make的终结任务也就是执行文件main
也就是说,main会一层一层的寻找文件的依赖关系,直到编译出一个目标文件。如果在查找过程中依赖的文件找不到那么就会直接退出或报错。

继续来看下之前的makefile文件。在第一条规则的时候。.o文件被重复了两次。如果工程需要加入一个新的.o文件,那么就需要在2个地方加。如果makefile很复杂。那么就可有可能忘掉一个需要加入的地方。而导致编译失败。所以为了makefile的易维护,在makefile中可以使用变量。可以理解为C语言中的宏定义
main:main.o calc.o
    gcc -o main main.o calc.o
文件修改如下:
objects=main.o calc.o
main:$(objects)
    gcc -o main $(objects)

make编译一的更多相关文章

  1. TODO:macOS编译PHP7.1

    TODO:macOS编译PHP7.1 本文主要介绍在macOS上编译PHP7.1,有兴趣的朋友可以去尝试一下. 1.下载PHP7.1源码,建议到PHP官网下载纯净到源码包php-7.1.0.tar.g ...

  2. Centos6.5下编译安装mysql 5.6

    一:卸载旧版本 使用下面的命令检查是否安装有MySQL Server rpm -qa | grep mysql 有的话通过下面的命令来卸载掉 rpm -e mysql //普通删除模式 rpm -e ...

  3. CENTOS 6.5 平台离线编译安装 PHP5.6.6

    一.下载php源码包 http://cn2.php.net/get/php-5.6.6.tar.gz/from/this/mirror 二.编译 编译之前可能会缺少一些必要的依赖包,加载一个本地yum ...

  4. CENTOS 6.5 平台离线编译安装 Mysql5.6.22

    一.下载源码包 http://cdn.mysql.com/archives/mysql-5.6/mysql-5.6.22.tar.gz 二.准备工作 卸载之前本机自带的MYSQL 安装 cmake,编 ...

  5. Android注解使用之注解编译android-apt如何切换到annotationProcessor

    前言: 自从EventBus 3.x发布之后其通过注解预编译的方式解决了之前通过反射机制所引起的性能效率问题,其中注解预编译所采用的的就是android-apt的方式,不过最近Apt工具的作者宣布了不 ...

  6. Hawk 6. 编译和扩展开发

    Hawk是开源项目,因此任何人都可以为其贡献代码.作者也非常欢迎使用者能够扩展出更有用的插件. 编译 编译需要Visual Stuido,版本建议使用2015, 2010及以上没有经过测试,但应该可以 ...

  7. android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!从此在andrid中自由使用 图像匹配、识别、检测

    目录: 1,过程感慨: 2,运行环境: 3,准备工作: 4,编译 .so 5,遇到的关键问题及其解决方法 6,实现效果截图. (原创:转载声明出处:http://www.cnblogs.com/lin ...

  8. 在Windows上编译和调试CoreCLR

    生成CoreCLR - Windows篇 本文的唯一目的就是让你运行Hello World 运行环境 Window 7+ Visual studio 2015 确保C++ 工具已经被安装,默认是不安装 ...

  9. 【踩坑速记】二次依赖?android studio编译运行各种踩坑解决方案,杜绝弯路,总有你想要的~

    这篇博客,只是把自己在开发中经常遇到的打包编译问题以及解决方案给大家稍微分享一下,不求吸睛,但求有用. 1.大家都知道我们常常会遇到dex超出方法数的问题,所以很多人都会采用android.suppo ...

  10. Windows下Visual studio 2013 编译 Audacity

    编译的Audacity版本为2.1.2,由于实在windows下编译,其源代码可以从Github上取得 git clone https://github.com/audacity/audacity. ...

随机推荐

  1. Laravel之哈希/常用函数/分页

    一.哈希 1.简介Laravel Hash 门面为存储用户密码提供了安全的Bcrypt 哈希算法.如果你正在使用Laravel 应用自带的AuthController 控制器,将会自动为注册和认证使用 ...

  2. Python魔法师

    第一章:数据结构和算法 1.1 查找最大或者最小的n个元素 heapq 模块的两个函数 nlargest()  nsmallest() import heapq nums = [1, 8, 2, 23 ...

  3. Python中的import和from import

    一.Python路径介绍 在python用import或者from...import来导入相应的模块. 模块其实就是一些函数和类的集合文件,它能实现一些相应的功能,当我们需要使用这些功能的时候,直接把 ...

  4. SQL中拆分字符串substr及统计字符出现频数replace用法实例讲解

    一.拆分字符串为若干行 例一:要求将表emp中的'king'按照每行一个单词拆成四行 注意:substr(str,pos):截取pos位置开始的字符: substr(str,pos,len):从pos ...

  5. c#创建对象并动态添加属性

    //动态类,可以作为基类被继承 dynamic backObj = new ExpandoObject(); //ExpandoObject 为密封类 backObj.image0 = IMGNAME ...

  6. About the Apple Captive Network Assistant

    If you’re a mac user, you likely have seen a strange popup window appear on your computer when you t ...

  7. visual studio 2010 LNK1123解决方式

    ------------------------------------------------------------Lysen----------------------------------- ...

  8. idea实时编译代码

    实时编译就不解释了,出现错误的代码在命令行能给你提示出来.看图操作吧:

  9. Webpack与Gulp、Grunt区别

    Webpack与Gulp.Grunt没有什么可比性,它可以看作模块打包机,通过分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等 ...

  10. oracle索引的理解

    1.当查询表时where条件中有多个索引时,优先使用主键索引,其它索引会失效. 2.当查询的返回的数据占总量数据的百分比小于20%时,建索引才有效果 3.不是主键的索引值可以为空,主键索引不能为空. ...