GCC相关常识
1. GCC、gcc 编译器、g++编译器
GCC原名为GNU C语言编译器(GNU C Compiler),只能处理C语言。但其很快扩展,变得可处理C++,后来又扩展为能够支持更多编程语言,如Fortran、Pascal、Objective -C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection)来自百度百科操作系)
gcc 是 GNU 的c编译器,是GNU编译器套件GCC(GNU Compiler Collection)的其中一个。
g++ 是 GNU 的c++编译器,是GNU编译器套件GCC(GNU Compiler Collection)的其中一个。
gcc 和 g++ 在编译的时候有一些区别,但是大体流程类似。
2. gcc 、g++ 编译流程
在 gcc 或 g++ 执行编译的时候一般有 以下4步:
预处理
处理源代码文件中的预处理指令,另外将 main.c 文件生成 main.i 文件
gcc -E main.c -o main.i
编译
将预处理后的源代码转换为汇编语言。将main.i 文件生成 main.s 汇编文件,注意这里是大写S,十分重要。
gcc -S main.i -o main.s
汇编
将汇编代码转换成机器码。main.s 文件转换成 main.o 文件
gcc -c main.s -o main.o
链接
合并所有的汇编文件,然后生成一个ELF类型的可执行文件main
gcc main.o -o main
#查看main文件信息
file main
3. 静态库和动态库
库文件是源文件的一种存在形式,库文件又分为静态库和动态库。下面使用4个文件 file1.c、file2.c、head.h、hello.c 进行动态库和静态库制作和使用的演示。
其中文件file1.c 如下:
#include"head.h"
int sum(int a, int b)
{
return a+b;
}
其中文件file2.c 如下:
#include"head.h"
int mul(int a,int b)
{
return a*b;
}
其中文件head.h如下:
#include<stdio.h>
int sum(int a,int b);
int mul(int a,int b);
其中调用文件 hello.c 如下:
#include"head.h"
int main()
{
printf("1+2=%d\n",sum(1,2));
printf("2*2=%d\n",mul(2,2));
return 0;
}
3.1 静态库
静态库最终被打包到应用程序中,优点是速度快,方便移植,缺点是每个进程使用应用程序时都要拷贝一份静态库,浪费内存,而且静态库更新时,程序都要重新编译,导致用户的整个程序都要重新下载。
静态库的命名一般分为三个部分:
后缀为 .a
库名称 可以 自定义,例如hello
前缀为 lib
例如一个名为 hello 的静态库 为 libhello.a
静态库的制作和使用
使用 file1.c、file2.c制作静态库,其中 head.h 文件中有函数的声明,file1.c 和 file2.c 有函数的实现,最后使用 hello.c 去链接静态库文件。
步骤1:将 .c 源文件 汇编生成 .o 文件
gcc -c file1.c -o file1.o gcc -c file2.c -o file2.o
步骤2:使用打包工具 ar 将准备好的 .o 文件打包为 .a 文件
其中,ar工具可以添加 参数 r 更新 、c 创建、s建立索引
#生成静态库 libhello.a ar rcs libhello.a file1.o file2.o
静态库制作完成,使用时,需要结合头文件 head.h 和 libhello.a
步骤3:使用静态库,需要一些参数进行描述
-L:大诶儿,指定要连接的库所在目录
-l:小诶儿,指定链接时需要的静态库,去除前缀和后缀,例如:-lhello
-I:大哎,指定 hello.c 文件用到的头文件 head.h 所在的路径
gcc -o hello hello.c -L./ -lhello -I./
生成 hello 可执行文件
#执行
./hello
#输出
1+2=3
2*2=4
3.2 动态库
共享库在程序编译时不会连接到程序中,只有程序运行时才会被载入。不同程序调用相同的库,内存中只有一份共享库的拷贝。当动态库更新时,只需要跟新动态库这一个部分。优点是节省内存,更新方便。缺点是移植性差,加载速度比静态库慢。
动态库的命名一般分为三个部分:
后缀为 .so
库名称 可以 自定义,例如hello
前缀为 lib
例如一个名为 hello 的动态库 为 libhello.so
动态库的制作和使用
使用 file1.c、file2.c 制作动态库,其中 head.h 文件中有函数的声明,file1.c 和 file2.c 有函数的实现,最后使用 hello.c 去链接动态库文件。
步骤1:将 .c 源文件 汇编生成 .o 文件
其中 -fpic 参数是指创建与地址无关的编译程序 ( pic,position independent code)
gcc -fpic -c file1.c -o file1.o gcc -fpic -c file2.c -o file2.o
步骤2:生成动态库,添加参数 -share ,意思是生成动态库
gcc -shared file1.o file2.o -o libhello.so
步骤3:动态库使用,需要一些参数进行描述
-L:大诶儿,指定要连接动态库的所在目录
-l:小诶儿,去掉前缀后缀的指定动态库的名称,例如 -lhello
-I:大哎,指定 hello.c 用到的头文件 head.h 所在的路径
gcc hello.c -L./ -I./ -lhello -o hello
#此时会报错,因为找不到 libhello.so 库,报错信息:
./hello: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
#原因是系统加载动态库时是从默认的动态库地址开始找起
#查看可执行文件依赖的库文件
ldd hello
#输出,其中 libhello.so => not found
linux-vdso.so.1 (0x00007ffec773b000)
libhello.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4820e2f000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4821220000)
#解决方法有:
#1.将动态库拷贝到系统默认的动态库地址,通常是 /lib 或者 /usr/lib
#2.临时设置动态库地址
#export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
#3.永久设置动态库地址
#3.1
#把export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库路径,设置到/etc/profile文件中
#执行source /etc/profile 使配置文件生效
#3.2
#把export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库路径, 设置到∼/.bashrc文件中
#source ~/.bashrc 使配置文件生效
#这里是演示,所以我使用临时设置动态库地址
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
./hello
#输出
1+2=3
2*2=4