精华内容
下载资源
问答
  • 注意:(1)路径必须指向头文件所在的子文件夹,而不能直到父文件夹就结束(2)每个路径不需要加上双引号,输入了之后,vs会自动加上双引号,如果自己加可能vs无法识别双引号(3)如果是多个路径,路径直接用“;...
      1. 头文件
        添加方法:工程---属性---配置属性---c/c++---常规---附加包含目录(Additional Include Directories):加上头文件存放目录。
        注意:(1)路径必须指向头文件所在的子文件夹,而不能直到父文件夹就结束(2)每个路径不需要加上双引号,输入了之后,vs会自动加上双引号,如果自己加可能vs无法识别双引号(3)如果是多个路径,路径直接用“;”隔开。(4)在使用代码处引用这个头文件,#include "xxx.h"
      2. 静态库
        添加方法:添加文件引用的lib静态库路径:工程---属性---配置属性---链接器---常规---附加库目录(Additional Library Directory):加上lib文件存放目录。 然后添加工程引用的lib文件名:工程---属性---配置属性---链接器---输入---附加依赖项(Additional Dependencies):加上lib文件名
      3. 动态库
        把引用的dll放到工程的可执行文件所在的目录(DeBug文件夹)下,如不添加,编译连接不会报错,运行报错:无法找到***.dll文件。

    转载于:https://www.cnblogs.com/ling123/p/8625961.html

    展开全文
  • .h头文件 .lib动态链接库文件 .dll 动态链接库 .h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。 附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的编译和...
    .h头文件 .lib动态链接库文件 .dll 动态链接库 
    

    .h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。

    附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果也使动态连接的程序运行起来,有dll就够了。在开发和调试阶段,当然最好都有。

    .h .lib .dll三者的关系是:

    H文件作用是:声明函数接口

    DLL文件作用是: 函数可执行代码

    当我们在自己的程序中引用了一个H文件里的函数,编链器怎么知道该调用哪个DLL文件呢?这就是LIB文件的作用: 告诉链接器 调用的函数在哪个DLL中,函数执行代码在DLL中的什么位置,这也就是为什么需要附加依赖项 .LIB文件,它起到桥梁的作用。如果生成静态库文件,则没有DLL ,只有lib,这时函数可执行代码部分也在lib文件中

    目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。动态库一般会有对应的导入库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了。有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。

    一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。在动态库的情况下,有两个文件,而一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。

    -------------------------------------------------------------------------------------

    静态链接库(Lib)与动态链接库(DLL)的区别

    静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。

    静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。


    “每一个lib文件就是若干函数(假设只有函数)的定义”
    lib库有两种,一种是包含了函数所在DLL文件和文件中函数位置的信息,称为导出库;一种是包含函数代码本身,一般现有的DLL,用的是前一种库;以前在DOS下的TC/BC等,是后一种库。包含函数原型声明的,是头文件(.h)。

    “通过#include包含这些函数声明的头文件后,我们的应用程序就可以使用lib文件中的函数”

    还要指定编译器链接相应的库文件。在IDE环境下,一般是一次指定所有用到的库文件,编译器自己寻找每个模块需要的库;在命令行编译环境下,需要指定每个模块调用的库。

    “那他和直接给出那个函数定义的文件,比如.cpp文件,和头文件有什么区别,静态链接库有什么用”
    cpp文件是源代码,库文件是编译后的二进制代码,比如你可以调用Windows的API,但是不能看到其源代码一样。

    “还有不明白的是,静态链接库中的lib文件只要用到,则整个lib文件的内容都放进了exe文件中,那它是被编译进去还是链接的时候连接进去的呢?”
    是在链接的时候将lib链接到目标代码中。

    静态链接库(Lib)
    在VC++6.0中new一个名称为libTest的static library工程,

    并新建lib.h和lib.cpp两个文件,lib.h和lib.cpp的源代码如下:

    //文件:lib.h
    #ifndef LIB_H
    #define LIB_H
    extern "C" int add(int x,int y);   //声明为C编译、连接方式的外部函数
    #endif

    //文件:lib.cpp
    #include "lib.h"
    int add(int x,int y)
    {
    return x + y;
    }


      编译这个工程就得到了一个.lib文件,这个文件就是一个函数库,它提供了add的功能。将头文件和.lib文件提交给用户后,用户就可以直接使用其中的add函数了。

      标准Turbo C2.0中的C库函数(我们用来的scanf、printf、memcpy、strcpy等)就来自这种静态库。

    下面来看看怎么使用这个库,在libTest工程所在的工作区内new一个libCall工程。libCall工程仅包含一个main.cpp文件,它演示了静态链接库的调用方法,其源代码如下:

    #include <stdio.h>
    #include "..\lib.h"//不可丢失
    #pragma comment( lib, "..\\debug\\libTest.lib" )  //指定与静态库一起连接
    int main(int argc, char* argv[])
    {
    printf( "2 + 3 = %d", add( 2, 3 ) );
    }
      静态链接库的调用就是这么简单,或许我们每天都在用,可是我们没有明白这个概念。代码中#pragma comment( lib , "..\\debug\\libTest.lib" )的意思是指本文件生成的.obj文件应与libTest.lib一起连接

    -------------------------------------------------------------------------------------------

    用VC++生成静态库文件

    今天闲着没事做,自己写了一点小笔记,不知道对于新手有没用,高手就不用看了,作为新手的我斗胆来发表一个笔记,就是静态库文件的封装过程,使用VC++6.0编写,下面是正文,也许我的用语并不专业

    以前我们写C/C++源文件的时候,都是先将各个写好的源文件编译,编译生成的是目标文件机器码,即.obj文件.(目标文件的扩展名不一定是.obj文件).

    我们调用的标准C/C++函数机器码实际被封装于标准C/C++静态库文件中的.即那些扩展名为.lib的文件中.

    最后链接器将我们编译的各个目标文件里的机器码和静态库(标准C/C++库)中的函数机器码链接到一起形成一个扩展名为.exe的可执行文件模块.

    在这里我们叙述将C/C++源文件编译链接成一个静态库文件,但它不是可执行模块,它体内含有可执行机器码

    静态库文件就像一个仓库或者容器,里面封装了一些可执行机器码.这些机器码是我们用程序设计语言,比如C/C++源文件编译后生成的机器码.

    .下面将讨论将C/C++源文件编译并链接成一个静态库文件的过程,

    VC++6.0中选择File-New-Win32 Static Library,写好工程名创建好工作空间后再选择菜单中New-File来为工程添加C或者C++ 源文件.

    假如我们为该工程添加了一个名为lib_c.c和一个名为lib_cpp.cpp的源文件

    //lib_c.c中的内容

    extern int Add(int x,int y) //该函数是一个外部函数,任何文件都可以访问它

    {
        return x+y;

    }

    extern int data_c
    //
    这是一个外部全局变量,任何文件可以访问它

    //lib_cpp.cpp中的内容

    extern “C” int
    reduce(int x,int y)//
    这里加了个”C”表示允许C源文件访问这个C++函数代码

    {
        return x-y;

    }

    extern “C” int data_cpp=2;

    注意以下几点

    (1)“extern”关键字修饰在函数或全局变量的定义中时,表示该函数或全局变量任何文件可以访问,“extern”关键字可以省略不写,缺省下就是”extern”

    “extern”关键字修饰在函数声明或全局变量声明中时,表示限定当前文件只能引用用“extern”关键字修饰定义的函数或全局变量.

    (2)”static”关键字修饰在函数或全局变量的定义中时,表示该函数或全局变量只能由本文件中加了”static”关键字修饰的函数声明或全局变量声明来引用.

    ”static”关键字修饰在函数声明或全局变量声明中时,表示限定当前文件只能引用用“static”关键字修饰定义的函数或全局变量.

    (3)CPP源文件的函数和全局变量定义中加了个”C”表示允许C源文件访问该函数和全局变量.如果是C++源文件访它们的话则可加可不加.注意这”C”要大写.

    接下来就要将写好的C/C++源文件进行编译和链接,最后会生成一个扩展名为.lib的文件.该文件就是静态库文件了,该静态库文件是不能直接运行的,我们所编译的C/C++源文件的机器码就已经被封装进这个用VC++6.0创建的静态库文件里面去了.

    .如何将编写好的静态库文件像使用C/C++标准库那样使用,下面将继续讨论

    1.VC++6.0新建一个工程名为TEST,添加一个名为TEST.c的源文件到该工程,因为我们将测试一下,将我们编写的库文件里的函数或者全局变量的机器码链接到我们这个TEST.c源文件中去,假设我们生成的库文件名为TEST.lib,先拷贝如下范例代码到TEST.c

    //TEST.c

    #include <stdio.h>

    extern int
    Add(int x,int y); //
    当前文件只能访问“extern”关键字修饰定义的Add函数

    extern int
    reduce(int x,int y);// //
    当前文件只能访问“extern”关键字修饰定义的reduce函数

    #pragma comment(lib,"TEST.lib") //指示链接器到字符串所表示的文件路径中去找库文件

    int main()

    {
        printf("%d\n",Add(2,3));

        printf("%d\n",reduce(3,2));

        return 0;

    }

    这里我们要声明静态库中已知的函数或全局变量的声明

    #pragma comment(lib,"TEST.lib")这条指令告诉链接器到字符串所表示的路径下去找库文件,这里我将库文件放到了当前工程目录下.也可以不写这句.

    还有一种方法,可以直接在VC++6.0中设置依次选择toolsoptionsdirectorieslibrary files菜单或选项,填入库文件路径(只键入库文件所在目录路径而不能输入库文件名),这只是告诉链接器库文件所在目录的路径,还没告诉链接器库文件名,方法是VC++6.0中设置依次选择project-settings-linkobject/library modules: 这栏输入库文件名字然后就OK

    2.当用C++源文件的目标文件和库文件的代码链接时有一点小改变,这里就不浪费口舌了,假设我们新建了一个工程并添加了一个名为TEST.CPP的源文件,拷贝如下范例代码到TEST.CPP

    //TEST.cpp

    #include <stdio.h>

    extern “C” int
    Add(int x,int y); //
    表示引用的是C函数代码

    extern int
    reduce(int x,int y);

    #pragma comment(lib,"TEST.lib")

    int main()

    {
        printf("%d\n",Add(2,3));

        printf("%d\n",reduce(3,2));

        return 0;

    }

    在这个C++源文件里引用C函数代码同样要加个”C”,但是在C源文件引用C++函数代码不能加”C++”,编译会报错,只能在C++文件函数定义中加”C”.

    只有C++才支持这种引用方式,也许因为只有C++兼容C而没有C兼容C++这一原则.

     

    转载内容来源于...

    展开全文
  • .h头文件 .lib动态链接库文件 .dll 动态链接库 转自 http://blog.csdn.net/c395565746c/article/details/5402239 (1).h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。 附加依赖项的是...

    .h头文件 .lib动态链接库文件 .dll 动态链接库

    转自

    http://blog.csdn.net/c395565746c/article/details/5402239


    (1).h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。

    附加依赖项的是.lib 不是.dll 
    
    若生成了DLL ,则肯定也生成 LIB文件 
    
    

    如果要完成源代码的编译和链接,有头文件和lib就够了。
    如果也使动态连接的程序运行起来,有dll就够了。
    在开发和调试阶段,当然最好都有。

    .h .lib .dll三者的关系是:

    H文件 作用是:声明函数接口 
    
    DLL文件 作用是: 函数可执行代码 
    
    当我们在自己的程序中引用了一个H文件里的函数,编链器怎么知道该调用哪个DLL文件呢? 
    
    这就是LIB 文件的作用: 告诉链接器 调用的函数在哪个DLL中,函数执行代码在DLL中的什么位置 
    
    这也就是为什么需要 附加依赖项 .LIB文件 ,它起到桥梁的作用。
    
    如果生成静态库文件,则没有DLL ,只有lib,这时函数可执行代码部分也在lib文件中
    
    目前以lib后缀的库有两种,一种为静态链接库
    
    
    (Static  Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库
    
    
    (Import  Libary,以下简称“导入库”)。 
    
    静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。   
    动态库一般会有对应的导入库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了。有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。
    
    导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。 

    (2)一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。
    (3)在动态库的情况下,有两个文件,而一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。


    在VS2008中添加.LIB文件

    法一:在project属性->链接器->输入->附加依赖项

    在以上路径加入即可,多个lib名用空格隔开

     

    法二:直接在代码中表现:

    #pragma comment(lib,"winmm.lib")   // 加入winmm.lib库


    展开全文
  •  当考虑怎样总结这个头文件动态链接库的查找问题时,我想到了一个程序从生到死的历程。写过很程序,编译过很程序,也运行过很程序,对一个程序的从生到死,感觉很简单,也就没有做更的或者说深入的思考与...
    本文转自:
    http://blog.csdn.net/dlutxie/article/details/6776936 
    

              当考虑怎样总结这个头文件及动态链接库的查找问题时,我想到了一个程序从生到死的历程。写过很多程序,编译过很多程序,也运行过很多程序,对一个程序的从生到死,感觉很简单,也就没有做更多的或者说深入的思考与研究。也许我们习惯了在windows环境下的编程,在那里我们有很好的IDE,它能把一个工程组织得很好,直接点编译生成一个可执行文件,然后直接双击这个.exe文件或者创建一个快捷方式运行这个程序。以前可能我们也听说过,源码先要编译,然后链接,然后装载、运行,可我们很少去考虑这背后到底都发生了些什么,似乎也不用考虑那么多,因为我们的IDE实在是太智能了,因为我们已经很习惯了使用windows环境,因为在windows下安装个软件,我们几乎只需要点击下一步就可以了。

            这段时间在做linux系统下opencv2.0到ARM开发板的移植,在这里,我有很多问题不得不考虑,问题如下:

                 程序在编译时,源码所需要的库(静态库和动态库)及头文件编译器是去哪找的?(库及头文件的查找)

                 当输入一个命令时,系统时如何找到这个命令的?(命令的查找)

                 程序在运行时,它所需要的库是去哪找的?(动态链接库的查找)

           这就是一个程序的由生到死的过程中需要考虑的几个问题!

           在linux系统下,我们常常要自己通过源码安装一些库,装一些软件,第一件事该想到的,编译生成后的头文件,库或者程序我们该放到哪,是放到/lib  /usr/lib  /usr/include  /usr/bin  /usr/local/lib  /usr/local/include    /usr/local/bin等目录下吗?可能有些书会说自己安装的程序一般放在/usr/local目录下,放到这下面我可以省心的不用去修改一些环境变量或配置文件了,但接下来我们可能会想,要是我以后想删除我前面安装的软件呢?你还能想起你以前安装这个软件时它到底安装了哪些文件了吗?几乎不可能吧!

             所以当我想安装一个软件时我希望像在windows下一样,把这个软件安装在一个单独的目录下,比如说我要安装opencv2.0,那么我就在/usr/local目录下创建一个目录opnecv2.0,然后把所有相关的都安装到/usr/local/opencv2.0目录下,这样如果我以后不想要这个库时我就可以直接删掉这个文件夹就可以了。可在这里我们就有些问题不得不考虑了:如果我们把opencv安装到了/usr/local/opencv2.0这个目录下了,那编译器在编译包含有opencv2.0的库或头文件时,编译器能找着这些头文件和库吗?如果这是一个可运行文件,我在其它目录下运行这个文件,系统能找到这个文件吗?它是如何找到这个文件的呢?当其它包含有opencv有关函数的程序时,它是如何找到这些库的呢?由这就引发了我上面提到的三个问题。

             下面我们先来看第一个问题:程序在编译时,源码所需要的库及头文件编译器是去哪找的?

             在这里,其实有库的查找,和头文件的查找,下面先来讲头文件的查找。我们在写一个比较大型的程序时,总是喜欢把这些函数还有一些数据结构的声明放在一个文件中,我们把这种文件称为头文件,文件名以.h后缀结尾。在一些源文件里,我们可能要包含自己写的头文件,还有一些标准库的头文件比如说stdio.h等等。在编译的预处理阶段,预处理程序会将这些头文件的内容插到相应的include指令处,现在的问题是编译器是如何找到这些头文件的。

             1. 在编译时,我们可以用-I(i的大写)选项来指定头文件所在的目录,如:

    test.h内容如下:

    1. Struct student  
    2. {  
    3.     int  age;  
    4. };  
    Struct student
    {
        int  age;
    };

    main.c 内容如下:

    1. #include<stdio.h>   
    2. #include<test.h>   
    3. int main()  
    4. {  
    5.     structstudent st;  
    6.     st.age= 25;  
    7.     printf(“st.age=%d\n”,st.age);  
    8.     return0;  
    9. }  
    #include<stdio.h>
    #include<test.h>
    int main()
    {
        structstudent st;
        st.age= 25;
        printf(“st.age=%d\n”,st.age);
        return0;
    }

             可以把test.h放在与main.c同一个目录下,编译命令如下:

            xgy@ubuntu:~/tmp/workSpace/testincludedir$  gcc main.c -I./

            如果把test.h放在/usr/include/xgytest目录下,注意,xgytest是我自己建的一个目录

           编译命令如下:xgy@ubuntu:~/tmp/workSpace/testincludedir$  gcc main.c –I/usr/include/xgytest

           注意:在-I后可以有空格也可以没有空格,另外也可以指定多个目录,例如,tesh.h放在当前文件夹下,还有一个teacher.h放在 ./include目录下,则可以这样编译:

          xgy@ubuntu:~/tmp/workSpace/testincludedir$  gcc main.c -I ./ -I ./include/

               2. 设置gcc的环境变量C_INCLUDE_PATH、CPLUS_INCLUDE_PATH 、CPATH。

            C_INCLUDE_PATH编译 C 程序时使用该环境变量。该环境变量指定一个或多个目录名列表,查找头文件,就好像在命令行中指定 -isystem 选项一样。会首先查找 -isystem 指定的所有目录。

             CPLUS_INCLUDE_PATH编译 C++ 程序时使用该环境变量。该环境变量指定一个或多个目录名列表,查找头文件,就好像在命令行中指定 -isystem 选项一样。会首先查找 -isystem 指定的所有目录。

              CPATH 编译 C 、 C++ 和 Objective-C 程序时使用该环境变量。该环 境变量指定一个或多个目录名列表,查找头文件,就好像在命令行中指定-l 选项一样。会首先查找-l 指定的所有目录。

              假设test.h放在/usr/include/xgytest,则对C_INCLUDE_PATH做如下设置:

    export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/include/xgytest

    详细请况可以参考如下文章:

    http://blog.csdn.net/katadoc360/article/details/4151286

     http://blog.csdn.net/dlutxie/article/details/8176164

    3. 查找默认的路径/usr/include   /usr/local/include等

     

    总结一下gcc在编译源码时是如何寻找所需要的头文件的:

       1.  首先gcc会从-Idir   -isystem dir   -Bprefix    -sysroot  dir     --sysroot=dir    -iquote dir选项指定的路径查找(这些选项先指定的会先搜索,有特例的情况请参考前面的链接)

        2. 然后找gcc的环境变量:C_INCLUDE_PATH、CPLUS_INCLUDE_PATH 、CPATH、GCC_EXEC_PREFIX等。(这些环境变量搜索的先后顺序不确定,有待确认)

       3. 然后查找GCC安装的目录(可以通过gcc  -print-search-dirs查询)

       4.  然后再按照下面列出的顺序查找系统默认的目录:/usr/include      /usr/local/include

             

    程序在编译时,编译器又是如何查找所需要的库的呢?这里的库既包括静态库又包括动态库。在这里,我们得先了解两个概念:库的链接时路径和运行时路径。

            现代连接器在处理动态库时将链接时路径(Link-time path)和运行时路径(Run-time path)分开,用户可以通过-L指定连接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。

    我们来看几个例子:

    pos.c文件的内容如下:

    1. #include<stdio.h>   
    2. void pos()  
    3. {  
    4.     printf("the directory is .//n");  
    5. }  
    #include<stdio.h>
    void pos()
    {
        printf("the directory is .//n");
    }

    main.c文件的内容如下:

    1. #include<stdio.h>   
    2. intmain()  
    3. {  
    4.     pos();  
    5.     return 0;  
    6. }  
    #include<stdio.h>
    intmain()
    {
        pos();
        return 0;
    }

    接下来看如下执行的命令:

    我们来分析下上面图片中的命令:生成的动态链接库libpos.so放在了当前的路径下,接着用gcc main.c  –lpos 来链接这个库却发现ld找不着这个库!然后我加了一个-L选项,指出这个库在当前路径下,结果编译通过,可在运行刚编译生成的a.out时又出现了错误!这就是运行是的链接错误!运行时的链接问题在后面将有介绍。用ldd命令可以查看一个可执行文件依懒于哪些库。注意-lpos, 这里的-l是L的小写,另外也可以写成-l  pos即中间有一个空格,但有没有空格是有一点区别的,有空格的只搜索与POSIX兼容的库,一般建议使用没有空格的。

             另外我们可以把刚才编译生成的libpos.so拷到默认的路径/lib  /usr/lib /usr/local/lib路径下,然后直接执行gcc main.c –lpos也可以通过编译。

             在这里补充说明一点:Linux下 的库文件在命名时有一个约定,那就是库文件应该以lib三个字母开头,由于所有的库文件都遵循了同样的规范,因此在用-l(L的小写字母)选项指定链接的库文件名时可以省去 lib三个字母,也就是说GCC在对-lfoo进行处理时,会自动去链接名为libfoo.so的文件。

    每个共享库也有一个实名,其真正包含有库的代码,组成如下:

    so+.+子版本号+.+发布号(最后的句点和发布号是可选项。)

    另外,共享库还有一个名称,一般用于编译连接,称为连名(linkername),它可以被看作是没有任何版本号的so名。在上面的讨论中,我一直是以动态库(或者说共享库)为例的,其实对于静态库也一样,只是在这里又有一个问题,如果在同一个目录下既有动态库,又有静态库,且它俩的文件名也一样,只是后缀不一样,那链接器在链接时是链接动态库还是链接静态库呢?如果我要指定链接动态库或者静态库又该如何做呢?

               让我们来看看下面执行的命令(注意下,为了方便,我开了两个终端):

    通过上面的这些命令,也许就能回答我刚提出的两个问题了。

    在这里,我还想看下编译时gcc是否会查LD_LIBRARY_PATH环境变量,还有/etc/ld.so.conf文件指定的路径,命令如下:

    从上面的命令可以看出,编译时,编译器不会查找LD_LIBRARY_PATH,还有/etc/ld.so.conf文件中指定的路径。下面来总结下:

     

    程序在编译链接时,编译器是按照如下顺序来查找动态链接库(共享库)和静态链接库的:

    1.  gcc会先按照-Ldir    -Bprefix选项指定的路径查找

    2. 再找gcc的环境变量GCC_EXEC_PREFIX

    3. 再找gcc的环境变量LIBRARY_PATH

    4. 然后查找GCC安装的目录(可以通过gcc  -print-search-dirs查询)

    5.  然后查找默认路径/lib

    6.  然后查找默认路径/usr/lib

    7.  最后查找默认路径/usr/local/lib

    8.  在同一个目录下,如果有相同文件名的库(只是后缀不同),那么默认链接的是动态链接库,可以用-static选项显示的指定链接静态库。

     

     第二个问题:当输入一个命令时,系统时如何找到这个命令的?(命令的查找)

        如果我们输入一个命令时带入路径时一般是不会不什么疑问的,因为此时我们执行的就是指定路径下程序。当我们只输入一个命令名时会发生什么情况呢?

    当我们键入命令名时,linux系统更确切的说应该是shell按照如下顺序搜索:

    1.  Shell首先检查命令是不是保留字(比如for、do等)

    2.  如果不是保留字,并且不在引号中,shell接着检查别名表,如果找到匹配则进行替换,如果别名定义以空格结尾,则对下一个词作别名替换,接着把替换的结果再跟保留字表比较,如果不是保留字,则shell转入第3步。

    3.  然后,shell在函数表中查找该命令,如果找到则执行。

    4.  接着shell再检查该命令是不是内部命令(比如cd、pwd)

    5.  最后shell在PATH中搜索以确定命令的位置

    6.  如果还是找不到命令则产生“command not found”错误信息。

    这里要注意一点:系统在按PATH变量定义的路径搜索文件时,先搜到的命令先执行。例如,我的PATH变量如下:

    root@ubuntu:~# echo $PATH

    /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:

    如果在不同的目录中有两个ls文件,例如/usr/local/sbin/ls, /usr/local/bin/ls,那么在使用ls的时候,会执行/usr/local/sbin/ls,因为在PATH中哪个目录先被查询,则哪个目录下的文件就会先执行。

     

    第三个问题:程序在运行时,它所需要的库是去哪找的?(动态链接库的查找)

            在这里我没有提到头文件的查找,因为头文件只在编译的时候才会用到,编译完后就不需要头文件了!另外,这里的库指的是动态链接库,静态链接库在链接后是不需要了的,因为链接时链接器会把静态库中的代码插入到相应的函数的调用处,所以程序在运行时不再需要静态库,而对于动态库来说,链接时,并没有将动态库中的任何代码或数据拷贝到可执行文件中,而只是拷贝了一些重定位与符号表信息!所以程序在运行时才需要链接时所使用的动态链接库以执行动态链接库中的代码!这个可以参考《深入理解计算机系统》第七章。

     

    程序运行时动态库的搜索路径搜索的先后顺序是:

    1.编译目标代码时指定的动态库搜索路径(指的是用-wl,rpath或-R选项而不是-L);

    example: gcc -Wl,-rpath,/home/arc/test,-rpath,/lib/,-rpath,/usr/lib/,-rpath,/usr/local/lib test.c

    2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径;

    3.配置文件/etc/ld.so.conf中指定的动态库搜索路径;

    4.默认的动态库搜索路径/lib;

    5.默认的动态库搜索路径/usr/lib。

    在上述1、2、3指定动态库搜索路径时,都可指定多个动态库搜索路径,其搜索的先后顺序是按指定路径的先后顺序搜索的。

     

    上面这个的具体内容可以参考:

    http://hi.baidu.com/kkernel/blog/item/ce31bb34a07e6b46251f14cf.html

             在这里补充说明下:gcc的-Wl,rpath选项可以设置动态库所在路径,也就是编译生成的该程序在运行时将到-Wl,rpath所指定的路径下去寻找动态库,如果没找到则到其它地方去找,并且这个路径会直接写在elf文件(就是生成的可执行文件)中,这样可以免去设置LD_LIBRARY_PATH。注意,gcc参数设定时-Wl,rpath,/path/to/lib, 中间不能有空格。

    gcc -o pos main.c -L. -lpos -Wl,-rpath,./

    上面这个命令的意思是:编译main.c时在当前目录下查找libpos.so这个库,生成的文件名为pos,当执行pos这个文件时,在当前目录下查找所需要的动态库文件。

    可以像下面这个命令一样指定查找多个路径:

    gcc -Wl,-rpath,/home/arc/test,-rpath,/lib/,-rpath,/usr/lib/,-rpath,/usr/local/libtest.c

    更改/etc/ld.so.conf文件后记得一定要执行命令:ldconfig!该命令会将/etc/ld.so.conf文件中所有路径下的库载入内存中。

     

    下面对编译时库的查找与运行时库的查找做一个简单的比较:

    1. 编译时查找的是静态库或动态库,而运行时,查找的只是动态库。

    2. 编译时可以用-L指定查找路径,或者用环境变量LIBRARY_PATH,而运行时可以用-Wl,rpath或-R选项,或者修改/etc/ld.so.conf文件或者设置环境变量LD_LIBRARY_PATH.

    3. 编译时用的链接器是ld,而运行时用的链接器是/lib/ld-linux.so.2.

    4. 编译时与运行时都会查找默认路径:/lib  /usr/lib

    5. 编译时还有一个默认路径:/usr/local/lib,而运行时不会默认找查该路径。

               如果安装的包或程序没有放在默认的路径下,则使用mancommand查找command的帮助时可能查不到,这时可以修改MANPATH环境变量,或者修改/etc/manpath.config文件。如果使用了pkg-config这个程序来对包进行管理,那么有可能要设置PKG_CONFIG_PATH环境变量,这个可以参考:http://www.linuxsir.org/bbs/showthread.php?t=184419

     

    写在最后的话

        一个程序的从生到死会发生很多很多的故事,在这里,我只是从一个角度探讨了其中的冰山一角,还有许许多多的问题需要去理解,比如说:编译链接时,各个文件是如何链接到一起的?程序运行时,动态库已经被加载到内存中,程序又是如何准确找到动态库在内存中的位置的?动态库的链接器/lib/ld-linux.so.2自己本身也是一个动态库,那么它又是如何被载入内存的呢?更深入的想一下,可以认为ld-linux.so.2是随内核一起载入内存的,那内核又是如何载入内存的呢?如果说内核是由bootloader载入的,那bootloader又是如何载入内存的呢?也许你该想到BIOS了。其中的一些问题可以参考《深入理解计算机系统》这本书。

    展开全文
  • 一般我们在Linux下执行某些外部程序的时候可能会提示找不到共享的错误, 比如: tmux: error while loading shared libraries: libevent-1.4.so.2: cannot open shared object file: No such file or ...
  • 还是要把Tensorflow包含在自己的工程下面,所以主要的操作就是链接自己文件夹中的文件,抽取Tensorflow需要的头文件,看了很博客,都是一带而过,具体如何操作呢? 新建一个头文件路径, mkdir inc cd inc mkdir...
  • 当考虑怎样总结这个头文件动态链接库的查找问题时,我想到了一个程序从生到死的历程。写过很程序,编译过很程序,也运行过很程序,对一个程序的从生到死,感觉很简单,也就没有做更的或者说深入的思考与...
  • 目录 一.#include <>与#include “” 二.gcc指定头文件的三种情况: 三.Linux指定动态路径 四、gcc -l 对动态和静态的使用方法是一样的,同一个库如果同时存在...另外,还总结了,gcc动态链接的方...
  • 在使用gcc编译连接生成可执行文件时,经常会碰到变量未定义、链接时或者运行可执行文件时找不到相应的动态库等问题,本文首先介绍了gcc在编译时头文件路径相关选项以及搜索路径顺序,然后讨论了编译成可执行文件时...
  • 用于加、解密,用C++写的动态库。同时将动态库的调用封装为一类,方便大家的使用。本已经用于我自己做过的很程序,没有出现问题。
  • gcc指定头文件路径及动态链接库路径   本文详细介绍了linux 下gcc头文件指定方法,以及搜索路径顺序的问题。另外,还总结了,gcc动态链接的方法以及路径指定,同样也讨论了搜索路径的顺序问题。本文包含了很...
  • 一.#include <>与#include “” #include <>直接到系统指定的某些目录中去找某些头文件。...1.会在默认情况下指定到/usr/include文件夹(更深层次的是一相对路径,gcc可执行程序的路径是/usr/bin/g...
  • .h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。 附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果也使动态连接的...
  • 我们在写一比较大型的程序时,总是喜欢把一些函数还有一些数据结构的声明放在一文件中,我们把这种文件称为头文件,文件名以.h后缀结尾。在一些源文件里,我们可能要包含自己写的头文件,还有一些标准头文件...
  • 不一定需要。创建一个库一般处于一下两种...头文件是你这个库里面提供了那些接口可以供外界使用。 如果没有头文件,其他人无法使用,因为不知道函数方法的原型! 2、在为某些软件项目写插件,而这些项目软件是公司内部的;
  • 大名鼎鼎FFmpeg不用我解释,这是移植编译可以在NDK中使用FFmpeg + libx264静态链接库外加我所编译版本的FFmpeg头文件。你可以利用它在NDK做视频编码解码等工作,或者下载ffmpeg.c源码将其编译链接为ffmpeg可执行...
  • Python 调用动态链接库

    千次阅读 2018-05-30 22:55:24
    ctypes是Python调用c的动态链接库的一内置模块。 通过 CMake 构建动态链接库 项目结构 ├── CMakeLists.txt # CMake 构建配置文件 ├── library.h # 动态链接库头文件 └── library.cpp # 动态链接...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 64,210
精华内容 25,684
关键字:

动态链接库多个头文件