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

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

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

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

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

各个文件的布局如下:

head.h文件的代码:

[cpp] view plaincopy

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

head.cpp文件的代码:

[cpp] view plaincopy

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

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

[cpp] view plaincopy

  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”。完整代码为

[cpp] view plaincopy

  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文件写成

[cpp] view plaincopy

  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文件代码

[cpp] view plaincopy

  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文件代码

[cpp] view plaincopy

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

sub.cpp文件代码

[cpp] view plaincopy

  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的代码为.

[cpp] view plaincopy

  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编译多个不同目录下的文件以及静态库、动态库的使用

时间: 2024-10-25 08:51:56

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

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

首先两者读取所有文件的方法都是采用迭代的方式,首先用函数A的返回值判断目录下是否有文件,然后返回值合法则在循环中用函数B直到函数B的返回值不合法为止.最后用函数C释放资源. 1.打开目录 #include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); 先看Linux的,返回的是DIR*,因此出错时返回NULL(0).而这里不用关心DIR结构具体定义,只需要知道是对它进行操作(注意:DIR不是保存文

Linux中通过setfacl实现目录下创建文件

在Linux 系统中,可以通过setfacl 来实现目录下创建文件或子目录,并继承父目录的权限. 下面以 root 用普通用户 user1 .在目录/mnt下 [[email protected] mnt]# setfacl -m u:user1:rwx share        //为目录添加ower = user1 ,并赋予rwx 的权根. [[email protected]redhat-7 mnt]# setfacl -d -m  u:user1:rwx share    //为目录添加

[Linux] 批量转换整个目录下的文件编码为UTF-8;

[Linux] 批量转换整个目录下的文件编码为UTF-8: #!/bin/bash - #===============================================================================# #          FILE: conv.sh#  #         USAGE: ./conv.sh #  #   DESCRIPTION: 一个支持把整个目录递归转换GB2312为UTF-8的脚本: #  #       OPTIONS: 

在linux 系统中利用 facl 实现目录下创建文件继承父目录的权限的所属组

在linux 系统中,可以通过setfacl 来实现目录下创建文件或子目录,并继承父目录的权限. 下面以 root 用普通用户 user1 .在目录/mnt下 [[email protected] mnt]# setfacl -m u:user1:rwx share        //为目录添加ower = user1 ,并赋予rwx 的权根. [[email protected] mnt]# setfacl -d -m  u:user1:rwx share    //为目录添加默认的acl权限

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

1. 确保 project->build automatically 已经被选上. 2. 如果选上了,也不好使, 使用这一招: project->clean..->选第2个clean select project, 勾上start build immediatelly 3.删除现在的项目,提前设置好编译文件输出路径,重新导入源文件,设置eclipse为保存时编译,然后在保存的时候就可以自动编译了 4.如果项目里引了某个不用的jar包,而那个包又被你删了,就会出现不报错但怎么也编译不出来c

Lua 遍历Linux目录下的文件夹

代码如下,里面有注释,应该能看懂. function getFile(file_name) local f = assert(io.open(file_name, 'r')) local string = f:read("*all") f:close() return string end function writeFile(file_name,string) local f = assert(io.open(file_name, 'w')) f:write(string) f:cl

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

文章来源:http://www.cnblogs.com/xfiver/archive/2010/07/07/1772764.html 1.  IOException parsing XML document from ServletContext resource [/WEB-INF/classes/beans.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resour

linux ls列出目录下所有文件数量

查看统计当前目录下文件的个数,包括子目录里的. ls -lR| grep "^-" | wc -l[喝小酒的网摘]http://blog.hehehehehe.cn/a/12311.htm Linux下查看某个目录下的文件.或文件夹个数用到3个命令:ls列目录.用grep过虑.再用wc统计.举例说明:1.查看统计当前目录下文件的个数 ls -l | grep "^-" | wc -l 2.查看统计当前目录下文件的个数,包括子目录里的. ls -lR| grep &q

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

转自:http://rubyer.me/blog/1613/ 单个文件中查找替换很简单,就不说了.文件夹下所有文件中字符串的查找替换就要记忆了,最近部署几十台linux服务器,记录下总结. 查找文件夹下包含字符串的文件 例:查找/usr/local目录下所有包含”rubyer.me”的文件. grep -lr 'rubyer.me' /usr/local/* vim替换单个文件中所有字符串方法 例:替换当前文件中所有old为new :%s/old/new/g #%表示替换说有行,g表示替换一行中