make编译一
在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编译一的更多相关文章
- TODO:macOS编译PHP7.1
TODO:macOS编译PHP7.1 本文主要介绍在macOS上编译PHP7.1,有兴趣的朋友可以去尝试一下. 1.下载PHP7.1源码,建议到PHP官网下载纯净到源码包php-7.1.0.tar.g ...
- Centos6.5下编译安装mysql 5.6
一:卸载旧版本 使用下面的命令检查是否安装有MySQL Server rpm -qa | grep mysql 有的话通过下面的命令来卸载掉 rpm -e mysql //普通删除模式 rpm -e ...
- CENTOS 6.5 平台离线编译安装 PHP5.6.6
一.下载php源码包 http://cn2.php.net/get/php-5.6.6.tar.gz/from/this/mirror 二.编译 编译之前可能会缺少一些必要的依赖包,加载一个本地yum ...
- CENTOS 6.5 平台离线编译安装 Mysql5.6.22
一.下载源码包 http://cdn.mysql.com/archives/mysql-5.6/mysql-5.6.22.tar.gz 二.准备工作 卸载之前本机自带的MYSQL 安装 cmake,编 ...
- Android注解使用之注解编译android-apt如何切换到annotationProcessor
前言: 自从EventBus 3.x发布之后其通过注解预编译的方式解决了之前通过反射机制所引起的性能效率问题,其中注解预编译所采用的的就是android-apt的方式,不过最近Apt工具的作者宣布了不 ...
- Hawk 6. 编译和扩展开发
Hawk是开源项目,因此任何人都可以为其贡献代码.作者也非常欢迎使用者能够扩展出更有用的插件. 编译 编译需要Visual Stuido,版本建议使用2015, 2010及以上没有经过测试,但应该可以 ...
- android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!从此在andrid中自由使用 图像匹配、识别、检测
目录: 1,过程感慨: 2,运行环境: 3,准备工作: 4,编译 .so 5,遇到的关键问题及其解决方法 6,实现效果截图. (原创:转载声明出处:http://www.cnblogs.com/lin ...
- 在Windows上编译和调试CoreCLR
生成CoreCLR - Windows篇 本文的唯一目的就是让你运行Hello World 运行环境 Window 7+ Visual studio 2015 确保C++ 工具已经被安装,默认是不安装 ...
- 【踩坑速记】二次依赖?android studio编译运行各种踩坑解决方案,杜绝弯路,总有你想要的~
这篇博客,只是把自己在开发中经常遇到的打包编译问题以及解决方案给大家稍微分享一下,不求吸睛,但求有用. 1.大家都知道我们常常会遇到dex超出方法数的问题,所以很多人都会采用android.suppo ...
- Windows下Visual studio 2013 编译 Audacity
编译的Audacity版本为2.1.2,由于实在windows下编译,其源代码可以从Github上取得 git clone https://github.com/audacity/audacity. ...
随机推荐
- iPhone手机解锁效果&&自定义滚动条&&拖拽--Clone&&窗口拖拽(改变大小/最小化/最大化/还原/关闭)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Django——快速实现注册
前言 对于web开来说,用户登陆.注册.文件上传等是最基础的功能,针对不同的web框架,相关的文章非常多,但搜索之后发现大多都不具有完整性,对于想学习web开发的新手来说不具有很强的操作性:对于web ...
- unordered_map 与 map 的对比(转)
unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value.不同的是unordered_map不会根据key的大小进行排序, 存储时是根据key的ha ...
- 偶遇 smon 进程cpu 开销高异常分析
今天突然发现线上一台oracle 数据库 servercpu 跑的非常高.感觉不是非常正常,细致看了下.发现是smon 进程吃掉了一个cpu. 那么这个smon 进程究竟在倒腾啥玩意 对smon 进程 ...
- C# : 资源文件(多用于处理国际化)
1.建立Resource文件夹,添加资源文件 处理国际化的问题,我们可以添加多个资源文件,如下就是添加一个中文的,一个英文的. 2.向其中添加键值对. 3.取值 CultureInfo uiCultu ...
- c语言字符串赋值
char *p="asdf";能运行 定义一个字符指针,并用它指向常量字符串"asdf"的首地址 char *p;p="asdf"; 能运行 ...
- Notepad++搭配MinGW 配置编译运行C/C++
1. Notepad++与Dev-Cpp都能编译运行, 环境变量的设置: 在PATH中加入"Dev-Cpp的MinGW64下的bin", 这是寻找gcc编译器的路径. 新建LIBR ...
- Android下的HttpClient的使用(9.11)
1 http://liangruijun.blog.51cto.com/3061169/803097 备注:此博客较早,android 4.0之后不允许在UI线程进行网络操作,所以没有输出结果. ...
- The return types for the following stored procedures could not be detected
1.使用dbml映射数据库,添加存储过程到dbml文件时报错. 2.原因:存储过程中使用了临时表 3.解决方案 3.1 通过自定义表值变量实现 Ex: DECLARE @TempTable TABLE ...
- thinkphp5或3.2 php动态修改config配置文件永久保存
thinkphp默认的参数方法只能读取,或者动态修改不能永久修改. 这是自己摸索出来的特发出来给需要的朋友(懂的朋友别笑话,功能我自己使用是没任何问题).有些参数还是保存在配置文件方便快捷!不一定所有 ...