程序编译过程详解


程序编译

1. Linux下的程序编译

GCC(GNU Compiler Collection),是符合GPL和LGPL许可证发行的自由软件,也是类Unix和Mac OS X的标准编译器。GCC可处理C (gcc)、C++(g++)、Java等语言。

Intel Composer XE 2013,Intel公司为x86平台编译器,针对Intel专门做了优化,在其他平台上表现也不错。

PGI Accelerator,支持CUDA Fortran。已经被Nvidia收购。

2. 串行程序的编译和执行

程序编译流程如下

  • 编译:将源文件编译成目标文件
  • 连接:将目标文件连接成可执行文件
  • 执行

Linux系统中,可执行文件没有同一的后缀,系统从文件属性来区分。源代码和目标文件的后缀名要保持同意规范,以便区分识别。

#include<stdio.h>
int main()
{
	printf("Hello,World.\n");
    return 0;
}
gcc hello.c										#编译
./a.out											#执行

gcc hello.c -o hello							#自动执行了编译和连接
./hello

如果可执行文件的目录加入了PATH环境变量,可以直接使用可执行文件名。
export PATH=$PATH:/home/mzy
#待定,无法成功运行

gcc -c hello.c									#1.只执行编译,不执行连接,生成hello.o
gcc -o hello hello.o							#2.连接目标文件,生成可执行文件

2.1多个源文件的编译

主程序源文件main.c

#include<stdio.h>
int main ()
{
    int sum=0,r,i;
    for (i = 1;i <= 10; ++i)
    {
        r = fun(i);
        sum += r;
    }
    printf("sum is %d\n",sum);
    return 0;
}

子函数源文件fun.c

int fun(int i)
{
    int res = i * i;
    return res;
}
gcc -o sum main.c fun.c							#同时编译多个源文件
./sum											#执行(自动执行了连接动作,自动删除了中间过程所产生的目标文件main.o和fun.o)

#也可以将源文件分别编译,最后再连接成可执行文件
gcc -c main.c
gcc -c fun.c
gcc -o sum main.o fun.o
./sum											#手动编译时目标文件不会消失


2.2 多个源文件使用头文件的例子

主程序文件main.c

#include<stdio.h>
#include"myhead.h"
int main()
{
    printf(STRING);
    return 0;
}

头文件myhead.h

#dedine STRING "Hello,Multi sources\n"
#编译时使用-I参数指定头文件搜索路径
gcc -c -I /home/mzy main.c
gcc -o Mut main.o

#也可以将以上两句话融合
gcc -o Multi -I /home/mzy main.c

2.3 Linux下函数库文件介绍

2.3.1 静态库与动态库

静态库

  • 命名规范为libXXX.a
  • 库函数会被连接进可执行程序,可执行文件体积较大
  • 可执行文件运行时,不需要从磁盘载入库函数,执行效率较高
  • 库函数更新后,需要重新编译可执行程序

动态库

  • 命名规范为libXXX.so
  • 库函数不被连接进可执行程序,可执行文件体积较小
  • 可执行文件运行时,库函数动态载入
  • 使用灵活,库函数更新后,不需要重新编译可执行程序
2.3.2 使用头文件

默认搜索头文件的目录优先级:

  1. 源文件所在目录(要求源文件中用#include "..." 格式指定)
  2. INCLUDE之类的环境变量指定的目录
  3. 编译器自己的头文件目录
  4. /usr/include操作系统头文件目录

如果想自己指定头文件搜索路径可以使用 -I 参数

  • 用 -I 指定的目录优先级比默认搜索目录高
  • -I 参数可以指定多个 -I -I ...
2.3.3 库函数的生成(静态库)

子函数fun1.c

int fun1 (int i)
{
    return i+i;
}

子函数fun2.c

int fun2 (int i)
{
    return i*i;
}
#编译子函数源代码
gcc -c fun1.c
gcc -c fun2.c
#使用ar命令将目标文件打包成静态库.a
ar cr libtest.a fun1.o fun2.o

2.3.4 库函数的生成(动态库)
#编译子函数源码,必须要使用-fPIC(Position-independent code)参数
gcc -c -fPIC fun1.c
gcc -c -fPIC fun2.c
#使用编译器-shared参数将目标文件连接成动态库.so
gcc -o libtest.so -shared fun1.o fun2.o
2.3.5 库函数的使用

主函数main.c,调用之前定义的fun1和fun2子函数

#include<stdio.h>
int main ()
{
    int i = 10,sum,product;
    sum = fun1(i);
    product = fun2(i);
    printf("the sum is %d,the product is %d\n",sum,product);
    return 0;
}
  1. gcc -c是编译,gcc -o才是连接。gcc -o时程序自动被编译,并且会删除中间产生的.o文件(目标文件)

  2. gcc -c -<参数> <要被编译的文件1> <要被编译的文件2> ...

    gcc -c -I <头文件的指定路径> <要被编译的文件>

  3. gcc -o <想要生成的可执行文件名> <需要的文件1> <需要的文件2>

    gcc -o <可执行文件名> -I <头文件路径> <需要被编译连接的文件1> <需要被编译连接的文件2> ...

    (因为-o参数默认会进行编译,所以上一行命令能够被执行。)

  4. ar cr libXXX.a <目标文件1> <目标文件2> ... #编译成静态库

    gcc -o <可执行文件名> <静态库的路径/libXXX.a> #在同级目录下也可以不写路径

  5. gcc -c -fPIC fun1.c #编译成动态库时必须指定-fPIC参数

    gcc -c -fPIC fun2.c

    gcc -o libXXX.so -shared <目标文件1> <目标文件2> #用-shared参数将目标文件连接成动态库.so

    2.3.5.1 库函数的使用方式一:直接使用

    gcc -c <需要编译的文件1> <需要编译的文件2> ...

    gcc -o <可执行文件名> <目标文件1> <目标文件2> <路径/libXXX.a> #当目标文件和静态库在同一目录时,可省略静态库的路径

    gcc -o <可执行文件名> <目标文件1> <目标文件2> <路径/libXXX.so> #不能省略动态库的路径,即使目标文件和动态库文件在同一目录下。

    2.3.5.2 库函数的使用方式二:在指定路径下搜索

    使用编译器的-L -lXXX 参数,表示在指定库函数路径下搜索名为libXXX.so或libXXX.a的库文件。

    • 如果库函数路径下同时有静态库和动态库,会选择动态库
    • -L 可以指定多次 -L -L ...
    • -L 指定的目录的搜索优先级最高
    • 如果在-L的目录中没有找到库函数,或者没有指定-L,编译器还会根据以下优先级从高到低搜索。
      1. LIBRARY_PATH(静态库)、LD_LIBRARY_PATH(动态库)环境变量指定路径
      2. 系统配置文件/etc/ld.so.conf中指定的动态库搜索路径
      3. 系统的/lib(64)、/usr/lib(64)等操作系统库文件目录
    gcc -c <源文件>
    gcc -o <可执行文件名> <目标文件> -L <库文件所在路径> -ltest
    

    ./Main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

    PS:如何解决在-L参数的指定目录下找不到动态库的情况?

    动态链接库的文件可能出现了问题,将文件加入到动态库的环境变量的路径中,

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/mzy
    #-L的目录中未找到库函数就会到动态库的环境变量中去找,避免了找不到文件的情况。
    

3.


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM