先看两篇博文,作为基础知识。如果对C/C++编译链接过程都了解的话,可以跳过不看。

http://www.firedragonpzy.com.cn/index.php/archives/2556

http://www.cppblog.com/shifan3/archive/2007/01/05/17325.html

一、  编译不同目录下的多个文件

各个文件的布局如下:

head.h文件的代码:

  1. #ifndef  HEAD_H
  2. #define  HEAD_H
  3. int add(int a, int b);
  4. #endif  /*HEAD_H*/

head.cpp文件的代码:

  1. #include    "head.h"
  2. int add(int a, int b)
  3. {
  4. return a + b;
  5. }

main.cpp文件的代码(head.h头文件还没包含

  1. #include  <iostream>
  2. using namespace std;
  3. int main(int argc, char *argv[])
  4. {
  5. cout<<add(3, 5)<<endl;
  6. return 0;
  7. }

1)  以相对路径的方式直接包含头文件

为了能够使用add函数,必须包含add所在的头文件。 最简单的方法是直接在main.cpp文件中,用相对路径包含head.h文件.即 #include”function/head.h”。完整代码为

  1. #include  <iostream>
  2. #include    "function/head.h"
  3. using namespace std;
  4. int main(int argc, char *argv[])
  5. {
  6. cout<<add(3, 5)<<endl;
  7. return 0;
  8. }

此时,编译命令为 :$g++ main.cpp function/head.cpp -o main

这种用相对路径包含头文件的方式有很多弊端。当function目录改成其它名字,或者head.h文件放到其它目录了,这时都要对main.cpp文件进行修改,如果head.h头文件被很多其它文件包含的话,这个工作量就大多了。

2)  用编译选项 –I(大写i)

其实,可以想一下,为什么iostream文件不在当前目录下,就可以直接使用呢?这是因为,编译器会在一些默认的目录下(/usr/include,/usr/inlucde/c++/4.4.3等目录)搜索头文件。所以,iostream头文件不用添加。但我们不能每写一个头文件就放到那里。

知道了原理,现在来用一下一个编译选项 –I(include的缩写)用来告诉编译器,还可以去哪里找头文件。

使用这个编译命令,$g++ main.cpp function/head.cpp -Ifunction -o main

此时main.cpp文件写成

  1. #include  <iostream>
  2. #include  <head.h>
  3. using namespace std;
  4. int main(int argc, char *argv[])
  5. {
  6. cout<<add(3, 5)<<endl;
  7. return 0;
  8. }

可以看到head.h文件是用<>而不是””。想一下C语言书中,两者的区别。这说明,用-I选项,相当于说明了一条标准路径。

3)  使用.o文件

此时,对于head.cpp文件,在编译命令中,还是要用到路径function/head.cpp。现在的想法是去掉这个。这时可以先根据head.cpp文件生成一个.o文件,然后就可以了去掉那个路径了。

先cd 到function目录。

输入命令:$g++ -c head.cpp -o head.o

生成一个head.o目标文件,

此时把生成的head.o文件复制到function的父目录,就是main.cpp所在的目录。

然后回到function的父目录,输入命令$g++ main.cpp head.o -Ifunction -o main

此时,直接使用head.o即可,无需head.cpp了。但头文件head.h还是要的。因为编译的时候要用到。链接的时候不用头文件。这个可以拆分成两条命令

$g++ -c main.cpp -Ifunction -o main.o

$g++ main.o head.o -o main

第一条是编译命令,后一条是链接命令。

二、  静态库

虽然上面说到的先生成.o目标文件,但如果function目录下有多个.cpp文件。那么就要为每一个.cpp文件都生成一个.o文件,这个工作量是会比较大。此时可以用静态库。静态库是把多个目标文件打包成一个文件。Anarchive(or static library) is simply a collection of object filesstored as a single file(摘自《Advanced Linux Programming》)。

下面介绍静态库

此时,文件布局为:

head.h文件代码

  1. #ifndef  HEAD_H
  2. #define  HEAD_H
  3. int add(int a, int b);
  4. int sub(int a, int b);
  5. #endif  /*HEAD_H*/

add.cpp文件代码

  1. #include    "head.h"
  2. int add(int a, int b)
  3. {
  4. return a + b;
  5. }

sub.cpp文件代码

  1. #include    "head.h"
  2. int sub(int a, int b)
  3. {
  4. return a - b;
  5. }

首先,生成.o目标文件。

cd进入function目录,输入命令$g++ -c sub.cpp add.cpp  这会分别为两个源文件目标文件,即生成sub.o和add.o文件。

然后,打包生成静态库,即.a文件。

输入命令$ar -cr libhead.a add.o sub.o

        注意:

        1)  命令中,.a文件要放到 .o文件的前面

        2)  .a文件的格式。要以lib作为前缀, .a作为后缀。

选项 c是代表create,创建.a文件。

r是代表replace,如果之前有创建过.a文件,现在为了提高性能而更改了add函数里面的代码,此时,就可以用r选项来代替之前.a文件里面的add.o

可以用命令$ar -t libhead.a 查看libhead.a文件里面包含了哪些目标文件。其执行结果自然为add.o  sub.o

现在回过头来关注main.cpp文件。

此时的main.cpp的代码为.

  1. #include  <iostream>
  2. #include  <head.h>
  3. using namespace std;
  4. int main(int argc, char *argv[])
  5. {
  6. cout<<add(3, 5)<<endl;
  7. cout<<sub(10, 6)<<endl;
  8. return 0;
  9. }

回到main.cpp文件所在的目录。

输入命令:$g++ main.cpp -Ifunction -Lfunction -lhead -o main 生成可执行程序

现在要解释一下使用静态库要用到的-L和-l(小写的L)选项。

-L表示要使用的静态库的目录。这和前面所讲的-I(大写i)差不多,就是用来告诉编译器去哪里找静态库。因为可能-L所指明的目录下有很多静态库,所以除了要告诉去哪里找之外,还要告诉编译器,找哪一个静态库。此时,就要用到-l(小写L)了。它用来说明链接的时候要用到哪个静态库。

        注意:

        1. 注意是使用-lhead,而不是-llibhead

命令中是使用-lhead,这是因为编译器会自动在库中添加lib前缀和.a后缀。

        2. 要把-l放到命令的尽可能后的位置,必须放到源文件的后面。

如果使用命令中的顺序,将出现下面错误。

还记得前面所链接的两篇博文的内容吧。当编译器在命令中碰到main.cpp文件后,会得到该文件的未解决符号表。然后,会在-l所指明的静态库中查找里面的每一个目标文件,把需要的部分抽取出来(编译器很聪明,不会全部统统接收)。但编译器只会对命令中的静态库查找一次,之后不再查找。如前面的错误命令那样,编译器先解析到-lhead。此时,未解决符号表都为空。编译器不会从libhead.a库中提取任何东西。当遇到main.cpp参数后,会得到未解决符号表。但此时已经错过libhead.a库了。编译器不会再次查找libhead.a库了。所以就出现错误了。(下面的动态库并不会出现这个问题。)

三、  动态库

使用命令$g++ -c -fPIC add.cpp sub.cpp生成位置无关的目标文件。

使用命令$g++ -shared -fPIC add.o sub.o -o libhead.so 生成.so动态链接库。

利用动态库生成可执行文件 $g++ -Ifunction -Lfunction -lhead main.cpp -o main

尝试运行. $./main 得到下面错误

这说明加载的时候没有找到libhead.so动态库。这是因为,Linux查找动态库的地方依次是

  1. 环境变量LD_LIBRARY_PATH指定的路径
  2. 缓存文件/etc/ld.so.cache指定的路径
  3. 默认的共享库目录,先是/lib,然后是/usr/lib

运行./main时,明显这三个地方都没有找到。因为我们没有把libhead.so文件放到那里。

其实,我们可以在生成可执行文件的时候指定要链接的动态库是在哪个地方的。

$g++ -Ifunction ./libhead.so main.cpp -o main

这个命令告诉可执行文件,在当前目录下查找libhead.so动态库。注意这个命令不再使用-L 和 -l了。

另外,还可以使用选项-Wl,-rpath,XXXX.其中XXXX表示路径。

四、  打造自己的库目录和头文件目录

        三个要解决的东西:指定编译时的头文件路径、指定链接时的动态库路径、指定运行时Linux加载动态库的查找路径

1.指定运行时Linux加载动态库的查找路径

利用前面所说的Linux程序运行时查找动态库的顺序,让Linux在运行程序的时候,去自己指定的路径搜索动态库。

可以修改环境变量LD_LIBRARY_PATH或者修改/etc/ld.so.cache文件。这里选择修改/etc/ld.so.cache文件。

1)  创建目录/mylib/so。这个目录可以用来存放自己以后写的所有库文件。由于是在系统目录下创建一个目录,所以需要root权限

2)  创建并编辑一个mylib.conf文件。输入命令$sudo vim /etc/ld.so.conf.d/mylib.conf

在mylib.conf文件中输入 /mylib/so

保存,退出。

3)  重建/etc/ld.so.cache文件。输入命令$sudo ldconfig

输入下面命令,生成main文件。注意,其链接的时候是用function目录下的libhead动态库。

$g++  -Ifunction -Lfunction -lhead main.cpp

直接运行./main。并没有错误。可以运行。说明,Linux已经会在运行程序时自动在/mylib/so目录下找动态链接库了。

2. 指定编译时的头文件路径

先弄清编译器搜索头文件的顺序。

1.先搜索当前目录(使用include””时)

2.然后搜索-I指定的目录

3.再搜索环境变量CPLUS_INCLUDE_PATH、 C_INCLUDE_PATH。两者分别是g++、gcc使用的。

4.最后搜索默认目录 /usr/include  和 /usr/local/include等

知道这些就简单了。输入下面命令。编辑该文件。

$sudo vim /etc/bash.bashrc  这个文件是作用了所有Linux用户的,如果不想影响其他用户,那么就编辑~/.bashrc文件。

打开文件后,去到文件的最后一行。输入下面的语句。

修改环境变量。然后保存并推出。

输入命令$bash 或者直接打开一个新的命令行窗口,使得配置信息生效。原理可以参考:http://blog.csdn.net/luotuo44/article/details/8917764

此时,可以看到 已经可以不用-I选项 下面编译命令能通过了。

$g++ -Lfunction -lhead mian.cpp -o main

3.指定链接时的动态库路径

需要注意的是,链接时使用动态库和运行时使用动态库是不同的。

同样先搞清搜索顺序:

1. 编译命令中-L指定的目录

2. 环境变量LIBRARY_PATH所指定的目录

3. 默认目录。/lib、/usr/lib等。

接下来和指定头文件路径一样。输入命令$sudo vim /etc/bash.bashrc   在文件的最后一行输入

保存,退出。

同样,输入bash,使得配置信息生效。

这是终极目标了。其中-lhead是不能省的。因为,编译器要知道,你要链接到哪一个动态库。当然,如果想像C运行库那样,链接时默认添加的动态库,那么应该也是可以通过设置,把libhead.so库作为默认库。但并不是所有的程序都会使用这个库。要是设置为默认添加的,反而不好。

Linux编译多个不同目录下的文件以及静态库、动态库的使用的更多相关文章

  1. linux 中 如何 搜索 指定目录 下 指定文件 的 指定内容

    开发时,经常遇到 全局查找某些代码 linux 中 如何 检索 某 目录下指定文件 的 指定内容如下: //.点为查找当前目录 下 的 所有 *.php 文件里 有 hello 的文件 find . ...

  2. Linux和Windows的遍历目录下所有文件的方法对比

    首先两者读取所有文件的方法都是采用迭代的方式,首先用函数A的返回值判断目录下是否有文件,然后返回值合法则在循环中用函数B直到函数B的返回值不合法为止.最后用函数C释放资源. 1.打开目录 #inclu ...

  3. linux下编译qt5.6.0静态库(使用./configure --help来看看都有哪些参数。超详细,有每一个模块的说明。如果改变了安装的目录,需要到安装目录下的bin目录下创建文件qt.conf)(乌合之众)good

    linux下编译qt5.6.0静态库 linux下编译qt5.6.0静态库 configure生成makefile 安装选项 Configure选项 第三方库: 附加选项: QNX/Blackberr ...

  4. [转帖]linux /proc目录下的文件为何无法用vi编辑保存

    linux /proc目录下的文件为何无法用vi编辑保存 https://blog.51cto.com/xlogin/1216914 学习一下 之前看过书 这一点 没太仔细看.. xlogin关注8人 ...

  5. Eclipse下无法自动编译,或者WEB-INF/classes目录下没文件,编译失败的解决办法(转载)

    文章来源:http://www.cnblogs.com/xfiver/archive/2010/07/07/1772764.html 1.  IOException parsing XML docum ...

  6. Eclipse下无法编译,或者WEB-INF/classes目录下没文件,编译失败的解决办法

    1. 确保 project->build automatically 已经被选上. 2. 如果选上了,也不好使, 使用这一招: project->clean..->选第2个clean ...

  7. 将windows共享文件夹挂载在linux机器的/mnt/windows/ 目录下进行访问

    将windows共享文件夹挂载在linux机器的/mnt/windows/ 目录下进行访问.windows机器ip:192.168.1.101,用户名:XXXX密码:XXXXlinux机器ip:ip2 ...

  8. Linux查找和替换目录下所有文件中字符串(转载)

    转自:http://rubyer.me/blog/1613/ 单个文件中查找替换很简单,就不说了.文件夹下所有文件中字符串的查找替换就要记忆了,最近部署几十台linux服务器,记录下总结. 查找文件夹 ...

  9. linux find-在指定目录下查找文件

    推荐:更多Linux 文件查找和比较 命令关注:linux命令大全 find命令用来在指定目录下查找文件.任何位于参数之前的字符串都将被视为欲查找的目录名.如果使用该命令时,不设置任何参数,则find ...

随机推荐

  1. D - 二叉树遍历(推荐)

    二叉树遍历问题 Description   Tree Recovery Little Valentine liked playing with binary trees very much. Her ...

  2. 引入工程报包导入异常:import javax.servlet.annotation.WebFilter;

    引入工程报包导入异常:import javax.servlet.annotation.WebFilter; (2013-02-21 16:38:00)   分类: java 今天上午导入了一个项目,用 ...

  3. BZOJ AC300题留念

  4. 一个Java程序的执行过程(转)

    我们手工执行java程序是这样的:  1.在记事本中或者是UE的文本编辑器中,写好源程序:  2.使用javac命令把源程序编译成.class文件:    编译后的.class(类字节码)文件中会包含 ...

  5. 基于visual Studio2013解决C语言竞赛题之0422牛顿迭代法

      题目

  6. 下载并在Eclipse中关联Android源代码

    大家都知道文档写的好当然让人非常舒服,可是有时候文档再好也不如直接看源代码来的直接,既然Android是开源的,为什么不在eclipse里直接看它的源代码呢? 1.下载源代码 这部分网上有大量的资料, ...

  7. SRM 577 Div II Level Two: EllysRoomAssignmentsDiv2

    题目来源: http://community.topcoder.com/tc?module=ProblemDetail&rd=15497&pm=12521 这个问题要注意的就是只需要直 ...

  8. 网页制作之html基础学习6-CSS浏览器兼容问题

    初学html和css时,每天切图,总会遇到很多浏览器兼容性问题.最近一直关注移动平台开发,就html和css来说,不用考虑那么多浏览器兼容性问题.到现在,以至于很多浏览器兼容性几乎忘光了.今天把以前总 ...

  9. xcode4.5.1、iphone5、ios6 使用记录

    链接地址:http://blog.sina.com.cn/s/blog_6123f9650101dmo7.html 1.修改工程名:直接选中工程名点一下,就像修改名称夹名称一样简单了.   2.导入旧 ...

  10. BZOJ AC 200题留念

    话说本来想200AC就把题目总结一下...但是我现在挺懒的..不想弄...以后再来吧.