精华内容
下载资源
问答
  • 注意:(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

    展开全文
  • 还是要把Tensorflow包含在自己的工程下面,所以主要的操作就是链接自己文件夹中的文件,抽取Tensorflow需要的头文件,看了很博客,都是一带而过,具体如何操作呢? 新建一个头文件路径, mkdir inc cd inc mkdir...

    还是要把Tensorflow包含在自己的工程下面,所以主要的操作就是链接自己文件夹中的库文件,抽取Tensorflow需要的头文件,看了很多博客,都是一带而过,具体如何操作呢?

    新建一个头文件路径,

    mkdir inc
    cd inc
    mkdir tf_14
    

    将对应头文件复制到对应的tf_14文件夹下

    cp -r your_path to_tf/bazel-genfiles/* ./inc/tf_14
    cp -r your_path_to_tf/tensorflow/cc ./inc/tf_14/tensorflow
    cp -r your_path_to_tf/tensorflow/core ./inc/tf_14/tensorflow
    cp -r your_path_to_tf/thirdparty ../inc/tf_14
    

    将库文件放到lib文件夹下

    mkdir lib
    cp -r your_path_to_tf/bazel-bin/tensorflow/libtensorflow_cc.so.14.1 ./lib/
    cp -r your_path_to_tf/bazel-bin/tensorflow/libtensorflow_frame.so.1.14.1 ./lib
    
    #建立对应的软链接
    ln -s libtensorflow_cc.so.1.14.1 libtensorflow_cc.so.1
    ln -s libtensorflow_cc.so.1 libtensorflow_cc.so
    ln -s libtensorflow_framework.so.1.14.1 libtensorflow_framework.so.1
    ln -s libtensorflow_framework.so.1 libtensorflow_framework.so
    

    写对应的Makefile,当中还有opencv的库,LZ就不删了。。。

    OURCE_ROOT = $(shell pwd)
    
    SOURCE = example_trainer.cc
    
    OBJS = test.o
    TARGET = test
    
    OBJS_DEBUG = test_debug.o
    TARGET_DEBUG = test_debug
    
    # compile and lib parameter
    CC      = g++
    
    LIBS += -ltensorflow_cc -ltensorflow_framework
    LIBS += -lopencv_highgui  -lopencv_imgcodecs -lopencv_calib3d -lopencv_imgproc -lopencv_flann -lopencv_core
    LIBS += -lcairo -lgtk-3 -lgdk_pixbuf-2.0 -lgobject-2.0
    LIBS +=  -llibjasper -lIlmImf -lippiw   -llibjpeg -llibpng -llibtiff  -lippicv -llibwebp -littnotify -lz
    LIBS +=  -ldc1394 -lraw1394 -lm -ldl
    
    #STATIC_LIBS =
    LDFLAGS += -L./lib -L./lib/opencvLib
    LDFLAGS += -L../boost/stage/lib -pthread
    INCLUDE += -I ./ -I ./inc -I ./inc/opencv -I ./
    INCLUDE += -I ./inc/tf_14 -I ./inc/tf_14/third_party
    
    
    CFLAGS  += -Wall -O -fPIC
    CXXFLAGS += -std=gnu++11
    
    all: $(TARGET) $(TARGET_DEBUG)
    
    # link
    $(TARGET):$(OBJS)
            $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) $(STATIC_LIBS) $(CFLAGS) $(INCLUDE) $(CXXFLAGS)
    
    $(TARGET_DEBUG):$(OBJS_DEBUG)
            $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) $(STATIC_LIBS) $(CFLAGS) $(INCLUDE) $(CXXFLAGS) -g
    
    #compile
    $(OBJS):$(SOURCE)
            $(CC) $(CFLAGS) $(INCLUDE)  $(CXXFLAGS) -o $@ -c $^
    
    $(OBJS_DEBUG):$(SOURCE)
            $(CC) $(CFLAGS) $(INCLUDE)  $(CXXFLAGS) -g -o $@ -c $^
    
    # clean
    clean:
            rm -fr *.o
            rm -fr $(TARGET) $(TARGET_DEBUG)
    
    

    编译的时候会报如下的错误;

    g++ -Wall -O -fPIC -I ./ -I ./inc -I ./inc/opencv -I ./ -I ./inc/tf_14 -I ./inc/tf_14/third_party  -std=gnu++11 -o test.o -c example_trainer.cc
    In file included from ./inc/tf_14/tensorflow/core/framework/tensor.h:21:0,
                     from ./inc/tf_14/tensorflow/cc/framework/ops.h:21,
                     from ./inc/tf_14/tensorflow/cc/ops/array_ops.h:8,
                     from ./inc/tf_14/tensorflow/cc/ops/standard_ops.h:19,
                     from example_trainer.cc:21:
    ./inc/tf_14/third_party/eigen3/unsupported/Eigen/CXX11/Tensor:1:42: fatal error: unsupported/Eigen/CXX11/Tensor: No such file or directory
     #include "unsupported/Eigen/CXX11/Tensor"
                                              ^
    compilation terminated.
    make: *** [test.o] Error 1
    
    

    缺了Eigen,补上就行

    cp -r  your_path_to_eigen/eigen ./inc/tf_14/eigen
    

    Makefile添加eigen头文件

    INCLUDE += -I ./inc/tf_14/eigen
    

    make然后继续报错:

    g++ -Wall -O -fPIC -I ./ -I ./inc -I ./inc/opencv -I ./ -I ./inc/tf_14 -I ./inc/tf_14/third_party -I ./inc/tf_14/eigen  -std=gnu++11 -o test.o -c example_trainer.cc
    In file included from ./inc/tf_14/tensorflow/core/framework/tensor.h:22:0,
                     from ./inc/tf_14/tensorflow/cc/framework/ops.h:21,
                     from ./inc/tf_14/tensorflow/cc/ops/array_ops.h:8,
                     from ./inc/tf_14/tensorflow/cc/ops/standard_ops.h:19,
                     from example_trainer.cc:21:
    ./inc/tf_14/tensorflow/core/framework/allocator.h:24:38: fatal error: absl/strings/string_view.h: No such file or directory
     #include "absl/strings/string_view.h"
                                          ^
    compilation terminated.
    make: *** [test.o] Error 1
    

    缺了absl,再加上

    cp -r your_path_to_absl/absl ./inc/tf_14/absl
    

    同时修改Makefile

    INCLUDE += -I ./inc/tf_14/absl
    

    make编译通过,运行一下:
    最后可以得到对应输出:
    在这里插入图片描述总算琢磨出来了。。。

    展开全文
  •  当考虑怎样总结这个头文件动态链接库的查找问题时,我想到了一个程序从生到死的历程。写过很程序,编译过很程序,也运行过很程序,对一个程序的从生到死,感觉很简单,也就没有做更的或者说深入的思考与...
    本文转自: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了。其中的一些问题可以参考《深入理解计算机系统》这本书。

    展开全文
  • 用于加、解密,用C++写的动态库。同时将动态库的调用封装为一类,方便大家的使用。本已经用于我自己做过的很程序,没有出现问题。
  • 当考虑怎样总结这个头文件动态链接库的查找问题时,我想到了一个程序从生到死的历程。写过很程序,编译过很程序,也运行过很程序,对一个程序的从生到死,感觉很简单,也就没有做更的或者说深入的思考与...

              当考虑怎样总结这个头文件及动态链接库的查找问题时,我想到了一个程序从生到死的历程。写过很多程序,编译过很多程序,也运行过很多程序,对一个程序的从生到死,感觉很简单,也就没有做更多的或者说深入的思考与研究。也许我们习惯了在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. };  

    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. }  

             可以把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. }  

    main.c文件的内容如下:

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

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

    我们来分析下上面图片中的命令:生成的动态链接库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了。其中的一些问题可以参考《深入理解计算机系统》这本书。

     

                                                                                                                                                                                   整理于2011-9-15     作者:seamus

    展开全文
  • 大名鼎鼎FFmpeg不用我解释,这是移植编译可以在NDK中使用FFmpeg + libx264静态链接库外加我所编译版本的FFmpeg头文件。你可以利用它在NDK做视频编码解码等工作,或者下载ffmpeg.c源码将其编译链接为ffmpeg可执行...
  • 当考虑怎样总结这个头文件动态链接库的查找问题时,我想到了一个程序从生到死的历程。写过很程序,编译过很程序,也运行过很程序,对一个程序的从生到死,感觉很简单,也就没有做更的或者说深入的思考与...
  • http://wendell07.blog.hexun.com/27431559_d.html 在Linux 下开发软件时,完全不使用第三方函数的情况是比较少见的,通常来讲都需要借助一个或多个函数的支持才能够完成相应的功能。从程序员的角度看,函数...
  • 关于程序运行库文件链接原理 文章目录关于程序运行库文件链接原理库和头文件的...库有两种,一种是 静态链接库,一种是 动态链接库,不管是哪一种库,要使用它们,都要在程序中包含相应的 include 头文件。我们先来回顾
  • 文章目录通过例子理解头文件库文件的编译链接 cpp 和 h 文件编译链接例子关于方法实现位置的一说明打包成静态链接库和动态链接库进行链接小小的总结 网传头文件的包含和静动态链接库的链接无非就是文件的赋值...
  • 当考虑怎样总结这个头文件动态链接库的查找问题时,我想到了一个程序从生到死的历程。写过很程序,编译过很程序,也运行过很程序,对一个程序的从生到死,感觉很简单,也就没有做更的或者说深入的思考与...
  • C程序链接多个动态库

    2020-09-15 20:41:23
    C程序链接多个动态库背景问题本地实例文件主库文件主程序 背景 做软件的过程中,需要多方提供API给自己的主程序使用,多方的API会以动态库+头文件的形式提供。 问题 将多个动态库文件,编译进项目需要的一个主的...
  • 但是动态链接库,对头文件和库都要进行相应的处理。 创建一项目,有几种方案,其中,在应用程序类型中选择dll,在附加项中选择导出符号,选择导出符号这样做比较方便,很东西都帮你做好了。 生成一dll.h dll....
  • 平时我们写程序都必须... 库有两种,一种是静态链接库,一种是动态链接库,不管是哪一种库,要使用它们,都要在程序中包含相应的include头文件。我们先来回顾一下程序编译的过程。如下图: ...
  • linux 动态链接库问题

    2018-05-31 23:10:38
    生成动态链接库时可以用多个.c文件生成一个动态链接库,主程序调用时,要分别include进头文件
  • c++动态库动态库,一个文件可以多个代码同时使用内存中只有一份,节省内存,可以随主代码一起编译。缺点是需要头文件。网友说:就是除了main函数之外的其他代码,都可以组成。2. 只介绍动态库(工作中主要用...
  • 一、编写合格的动态链接库头文件  C语言的头文件,可供一个或多个程序引用,里面一般定义程序所需的常量,自定义类型及函数原型说明等.其中的函数原型说明,则供编译器检查语法,用于排除引用参数时类型不一致的错误....
  • 一般而言,在vs中,很少使用源文件,大部分是使用对类进行声明的头文件(.h)和封装了类的链接库(静态.lib或动态.dll)。如果要使用这些类,需要在文件中包含头文件的名字,如#include “cv.h”。但这并不够,...
  • 在linux中,静态链接库为lib*.a,动态链接库为lib*.so。静态链接库文件比较大,动态链接库文件比较小。动态库,一个文件可以多个代码同时使用内存中只有一份,节省内存,可以随主代码一起编译。缺点是需要头文件。 ...
  • 打开的你VS,新建一个动态链接库(DLL) 文件 -> 新建 -> 项目 -> 选择Visual C++ -> Windows桌面 -> 动态链接库 我们可以观察一下在新建的项目中,已经包含了两个头文件和两个源文件 // framework.h...
  • 使用VC6.0搭建一个空白动态链接库项目,步骤如图,文件——》新建——》工程——》选择动态链接库,工程名自己随便填: 项目目录很简单,一个头文件和一个源文件就行了,图片也有目录结构显示。 网上有很博文...
  • 时我们写程序都必须include很多头文件,因为可以避免重复造轮子,软件大厦可不是单靠一人就能完成的。... 库有两种,一种是静态链接库,一种是动态链接库,不管是哪一种库,要使用它们,都要...
  • 库有两种,一种是静态链接库,一种是动态链接库,不管是哪一种库,要使用它们,都要在程序中包含相应的include头文件。我们先来回顾一下程序编译的过程。如下图: 我们结合gcc指令来看一下每阶段生成的文件: ...

空空如也

空空如也

1 2 3 4 5 ... 13
收藏数 246
精华内容 98
关键字:

动态链接库多个头文件