程序编译
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 使用头文件
默认搜索头文件的目录优先级:
- 源文件所在目录(要求源文件中用#include "..." 格式指定)
- INCLUDE之类的环境变量指定的目录
- 编译器自己的头文件目录
- /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;
}
-
gcc -c是编译,gcc -o才是连接。gcc -o时程序自动被编译,并且会删除中间产生的.o文件(目标文件)
-
gcc -c -<参数> <要被编译的文件1> <要被编译的文件2> ...
gcc -c -I <头文件的指定路径> <要被编译的文件>
-
gcc -o <想要生成的可执行文件名> <需要的文件1> <需要的文件2>
gcc -o <可执行文件名> -I <头文件路径> <需要被编译连接的文件1> <需要被编译连接的文件2> ...
(因为-o参数默认会进行编译,所以上一行命令能够被执行。)
-
ar cr libXXX.a <目标文件1> <目标文件2> ... #编译成静态库
gcc -o <可执行文件名> <静态库的路径/libXXX.a> #在同级目录下也可以不写路径
-
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,编译器还会根据以下优先级从高到低搜索。
- LIBRARY_PATH(静态库)、LD_LIBRARY_PATH(动态库)环境变量指定路径
- 系统配置文件/etc/ld.so.conf中指定的动态库搜索路径
- 系统的/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的目录中未找到库函数就会到动态库的环境变量中去找,避免了找不到文件的情况。