1 gcc的卸载与安装
# 卸载gcc
sudo apt-get remove gcc
# 安装gcc
sudo apt-get install gcc
# 查看安装是否成功
gcc -v
2 gcc组件
2.1 GCC 安装的各个部分
部分 | 描述 |
---|---|
c++ | gcc 的一个版木,默认语言设置为 C++,而且在连接的时候自动包含标准 C++ 库。这和 g++ 一样 |
ccl | 实际的C编译程序 |
cclplus | 实际的 C++ 编泽程序 |
collect2 | 在不使用 GNU 连接程序的系统上,有必要运行 collect2 来产生特定的全局初始化代码(例如 C++ 的构造函数和析构函数) |
configure | GCC 源代码树根目录中的一个脚木。用于设置配置值和创建GCC 编译程序必需的 make 程序的描述文件 |
crt0.o | 这个初始化和结束代码是为每个系统定制的,而且也被编译进该文件,该文件然后会被连接到每个可执行文件中来执行必要的启动和终止程序 |
cygwin1.dll | Windows 的共享库提供的 API,模拟 UNIX 系统调用 |
f77 | 该驱动程序可用于编译 Fortran |
f771 | 实际的 Fortran 编译程序 |
g++ | gcc 的一个版木,默认语言设置为 C++,而且在连接的时候自动包含标准 C++ 库。这和 c++ 一样 |
gcc | 该驱动程序等同于执行编译程序和连接程序以产生需要的输出 |
gcj | 该驱动程序用于编译 Java |
gnat1 | 实际的 Ada 编译程序 |
gnatbind | 一种工具,用于执行 Ada 语言绑定 |
gnatlink | 一种工具,用于执行 Ada 语言连接 |
jc1 | 实际的 Java 编译程序 |
libgcc | 该库包含的例程被作为编泽程序的一部分,是因为它们可被连接到实际的可执行程序中。 它们是特殊的例程,连接到可执行程序,来执行基木的任务,例如浮点运算。这些库中的例程通常都是平台相关的 |
libgcj | 运行时库包含所有的核心 Java 类 |
libobjc | 对所有 Objective-C 程序都必须的运行时库 |
libstdc++ | 运行时库,包括定义为标准语言一部分的所有的 C++ 类和函数 |
2.2 GCC 使用的软件工具
工具 | 描述 |
---|---|
addr2line | 给出一个可执行文件的内部地址,addr2line 使用文件中的调试信息将地址翻泽成源代码文 件名和行号。该程序是 binutils 包的一部分 |
ar | 这是一个程序,可通过从文档中增加、删除和析取文件来维护库文件。通常使用该工具是为了创建和管理连接程序使用的目标库文档。该程序是 binutils 包的一部分 |
as | GNU 汇编器。实际上它是一族汇编器,因为它可以被编泽或能够在各种不同平台上工作。 该程序是 binutils 包的一部分 |
autoconf | 产生的 shell 脚木自动配置源代码包去编泽某个特定版木的 UNIX |
c++filt | 程序接受被 C++ 编泽程序转换过的名字(不是被重载的),而且将该名字翻泽成初始形式。 该程序是 binutils 包的一部分 |
f2c | 是 Fortran 到C的翻译程序。不是 GCC 的一部分 |
gcov | gprof 使用的配置工具,用来确定程序运行的时候哪一部分耗时最大 |
gdb | GNU 调试器,可用于检查程序运行时的值和行为 |
GNATS | GNU 的调试跟踪系统(GNU Bug Tracking System)。一个跟踪 GCC 和其他 GNU 软件问题的在线系统 |
gprof | 该程序会监督编泽程序的执行过程,并报告程序中各个函数的运行时间,可以根据所提供 的配置文件来优化程序。该程序是 binutils 包的一部分 |
ld | GNU 连接程序。该程序将目标文件的集合组合成可执行程序。该程序是 binutils 包的一部 |
libtool | 一个基本库,支持 make 程序的描述文件使用的简化共享库用法的脚木 |
make | 一个工具程序,它会读 makefile 脚木来确定程序中的哪个部分需要编泽和连接,然后发布 必要的命令。它读出的脚木(叫做 makefile 或 Makefile)定义了文件关系和依赖关系 |
nlmconv | 将可重定位的目标文件转换成 NetWare 可加载模块(NetWare Loadable Module, NLM)。该 程序是 binutils 的一部分 |
nm | 列出目标文件中定义的符号。该程序是 binutils 包的一部分 |
objcopy | 将目标文件从一种二进制格式复制和翻译到另外一种。该程序是 binutils 包的一部分 |
objdump | 显示一个或多个目标文件中保存的多种不同信息。该程序是 binutils 包的一部分 |
ranlib | 创建和添加到 ar 文档的索引。该索引被 Id 使用来定位库中的模块。该程序是 binutils 包的一部分 |
ratfor | Ratfor 预处理程序可由 GCC 激活,但不是标准 GCC 发布版的一部分 |
readelf | 从 ELF 格式的目标文件显示信息。该程序是 binutils 包的一部分 |
size | 列出目标文件中每个部分的名字和尺寸。该程序是 binutils 包的一部分 |
strings | 浏览所有类型的文件,析取出用于显示的字符串。该程序是 binutils 包的一部分 |
strip | 从目标文件或文档库中去掉符号表,以及其他调试所需的信息。该程序是 binutils 包的一部 |
vcg | Ratfor 浏览器从文木文件中读取信息,并以图表形式显示它们。而 vcg 工具并不是 GCC 发布中的一部分,但 -dv 选项可被用来产生 vcg 可以理解的优化数据的格式 |
windres | Window 资源文件编泽程序。该程序是 binutils 包的一部分 |
3 gcc 与 g++的区别
# gcc编译cpp
gcc -xc++ -lstdc++ -shared-libgcc
# g++编译cpp
g++
# g++的语法更加严格
# gcc可以编译支持的所有代码,有后缀则gcc会自动识别,选定编译器
# 若没有后缀则需要-x命令
gcc -xc++ demo
文件名称+扩展名 | GCC 编译器识别的文件类型 |
---|---|
file.c | 尚未经过预处理操作的 C 源程序文件。 |
file.i | 经过预处理操作、但尚未进行编译、汇编和连接的 C 源代码文件。 |
file.cpp file.cp file.cc file.cxx file.CPP file.c++ file.C | 尚未经过预处理操作的 C++ 源代码文件。 |
file.ii | 已经预处理操作,但尚未进行编译、汇编和连接的 C++ 源代码文件。 |
file.s | 经过编译生成的汇编代码文件。 |
file.h | C、C++ 或者 Objective-C++ 语言头文件。 |
file.hh file.H file.hp file.hxx file.hpp file.HPP file.h++ file.tcc | C++ 头文件。 |
4 选择对应库编译
4.1 c标准
GCC 版本 | C语言常用标准 | |||||||
---|---|---|---|---|---|---|---|---|
C89/C90 | C99 | C11 | C17 | GNU90 | GNU99 | GNU11 | GNU17 | |
10.1 ~ 8.4 | c89 / c90 | c99 | c11 | c17/c18 | gnu90/gnu89 | gnu99 | gnu11 | gnu17/gnu18 |
7.5 ~ 5.5 | c89/c90 | c99 | c11 | gnu90/gnu89 | gnu99 | gnu11 | ||
4.9.4 ~ 4.8.5 | c89/c90 | c99 | c11 | gnu90/gnu89 | gnu99 | gnu11 | ||
4.7.4 | c89/c90 | c99(部分支持) | c11(部分支持) | gnu90/gnu89 | gnu99(部分支持) | gnu11(部分支持) | ||
4.6.4 | c89/c90 | c99(部分支持) | c1x(部分支持) | gnu90/gnu89 | gnu99(部分支持) | gnu1x(部分支持) | ||
4.5.4 | c89/c90 | c99(部分支持) | gnu90/gnu89 | gnu99(部分支持) |
4.2 c++的标准
CC 版本 | C++常用标准 | |||||||
C++98/03 | C++11 | C++14 | C++17 | GNU++98 | GNU++11 | GNU++14 | GNU++17 | |
10.1 ~ 8.4 | c++98/c++03 | c++11 | c++14 | c++17 | gnu++98/gnu++03 | gnu++11 | gnu++14 | gnu++17 |
7.5 ~ 5.5 | c++98/c++03 | c++11 | c++14 | c++1z(部分支持) | gnu++98/gnu++03 | gnu++11 | gnu++14 | gnu++1z(部分支持) |
4.9.4 ~ 4.8.5 | c++98/c++03 | c++11 | c++1y(部分支持) | gnu++98/gnu++03 | gnu++11 | gnu++1y(部分支持) | ||
4.7.4 | c++98 | c++11(部分支持) | gnu++98 | gnu++11(部分支持) | ||||
4.6.4 | c++98 | c++0x(部分支持) | gnu++98 | gnu++0x(部分支持) | ||||
4.5.4 | c++98 | c++0x(部分支持) | gnu++98 | gnu++0x(部分支持) |
4.3 调用规则
# 选择对应的标准库编译
gcc/g++ -std=编译标准
gcc -std=c89
# 对于 C 语言程序来说,其等价于 -std=c90;对于 C++ 程序来说,其等价于 -std=c++98。
-ansi
5 gcc的使用
5.1 一步生成a.out
gcc hello.c
5.2 生成目标程序
gcc hello.c -o hello
5.3 预处理
# 不进行错误检测,将宏、条件编译、头文件复制到程序对应位置
gcc hello.c -E -o hello.i
# -C 选项,阻止 GCC 删除源文件和头文件中的注释
gcc hello.c -E -C -o hello.i
# -U 选项取消一定要的宏
gcc hello.c -U 宏名 -E -C -o hello.i
# -D 定义宏
gcc hello.c -D 宏名 -E -C -o hello.i
# ==========头文件相关========
#-include 文件名 相当于#include "文件名"
gcc hello.c -include 文件名 -E -C -o hello.i
# -iquote 文件名 指定文件搜索路径
gcc hello.c -iquote 文件名 -E -C -o hello.i
# 指定搜索头文件的目录,适用于以引号 "" 和 <> 导入的头文件
gcc hello.c -I 文件名 -E -C -o hello.i
gcc hello.c -isystem 文件名 -E -C -o hello.i
gcc hello.c -idirafter 文件名 -E -C -o hello.i
# ========== #include ""头文件搜索顺序=======
# 首先搜索当前程序文件所在的目录,或-include
# 其次再前往 -iquote 选项指定的目录中查找;
# 前往 -I 选项指定的目录中搜索;
# 前往 -isystem 选项指定的目录中搜索;
# 前往默认的系统路径下搜索;
# 前往 -idirafter 选项指定的目录中搜索。
5.4 编译
# c语言转为汇编语言,进行错误检测
gcc hello.i -S -o hello.s
5.5 汇编
# 将汇编语言生成二进制目标文件
gcc hello.s -c -o hello.o
5.6 连接
# 连接二进制文件、启动代码、库代码生成可执行程序
gcc hello.o -o hello
5.7 只编译汇编不链接
# -g打印详细信息,编译32位平台加-m32
gcc hello.c -g -c -o hello.o
5.8 多文件编译
gcc *.c -o test
5.9 打印警告
gcc hello.c -Wall -o hello
5.9 反编译
# 获取中间文件
gcc -g -c hello.s -o hello.o
# 获得可执行程序
gcc hello.o -o hello
# 获取反编译文件:ELF 文件包含的指令和数据
objdump -D hello
# 获取反编译文件与 C 语言源代码混合
objdump -S hello
6 库的概念
-
库:实现编译好的二进制代码,可被程序调用。
-
库的分类:静态库与动态库。
7 静态库
7.1 静态库介绍
- 静态库:编译/连接时会将静态库中相关代码复制到可执行文件中。
7.2 静态库的特点
-
优点:程序运行时无需加载库,运行速度更快。
-
缺点:程序尺寸变大;静态库升级时程序需要重新编译链接。
7.3 静态库的创建与使用
# ===============生成静态库的参数
# c 禁止在创建库时产生的正常消息
# r 如果指定的文件已经存在于库中,则替换它
# s 无论 ar 命令是否修改了库内容都强制重新生成库符号表
# v 将建立新库的详细的逐个文件的描述写至标准输出
# q 将指定的文件添加到库的末尾
# t 将库的目录写至标准输出
# ===============静态库的制作
# 只编译不链接生成hello中间文件
gcc -g -c -o hello hello.c
# 创建静态库
ar rsv libhello.a hello # 库文件名: lib库名.a
# 查看静态库信息
nm libhello.a
# 一个文件调用静态库
gcc main.c -o test -L. -lhello
# 打印测试程序的链接库
ldd test
# ===============静态库的使用
# 如果是c库里的可以直接调用,而其他文件的需要加头文件与静态库地址、库名
# 创建文件与文件夹
#---include
#--------hello.h 头文件
#---src
#--------main.c 主函数
#---lib
#---------libhello.a 静态库
# 编译
gcc main.c -o test -I../include/ -L../lib -lhello
# 删除库也不会影响程序运行
7.4 调用顺序
- ① 有限到-L 选项指定的查找路径下查找。
- ② LIBRARY_PATH 环境变量指定的路径中搜索。
- ③ GCC 编译器默认的搜索路径: /lib、/lib64、/usr/lib、/usr/lib64、/usr/local/lib、/usr/local/lib64。
7.5 静态库动态库混合顺序
gcc -o test main.c -lxxx
# 先查找共享库,再查找静态库
gcc -o test main.c -lxxx -static
# 加static可以只查静态库
8 共享库
8.1 共享库介绍
- 共享库:编译/连接时仅仅记录使用到了那个库,不拷贝代码,在程序执行时加载该库。
8.2 共享库特点
-
优点:可以多个文件共享库,程序尺寸小,库升级时更方便。
-
缺点:程序执行速度变慢。
8.3 共享库的创建与使用
# ==========一步到位创建=====
gcc -fpic -shared 源文件名... -o 动态链接库名
gcc -fpic -shared hello.c -o libhello.so.1 # 版本号1
# shared 选项用于生成动态链接库
# -fpic表示各目标文件中函数、类等功能模块的地址使用相对地址,库在任何位置都能调用。
# ==========分步创建=========
gcc -c -fpic hello.c
gcc -shared -o libhello.so.1 hello.o
/* ===========调用问题===========
gcc main.c libhello.so -o test # 运行失败
ldd test # 查看找不到动态库
# ==============本终端回话使用======
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/hgfs/arm_c/HQ/1_lib/2_dynamic_lib/src/
export # 查看是否成功
# ==============本用户使用========
~/.bashrc 或~/.bash_profile 文件
sudo vim ~/.bashrc 或者~/.bash_profile
# 在尾部加一句
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/hgfs/arm_c/HQ/1_lib/2_dynamic_lib/src/
# 重启配置文件
source ~/.bashrc
# ==============所有用户使用=======
# 将链接库文件移动到标准库目录下(例如 /usr/lib、/usr/lib64、/lib、/lib64)
8.4 库的搜索顺序
- ① 指定动态库了搜索路径:-Wl,-rpath=路径1 : 路径2,则有限查找。
- ② 查不到就到LD_LIBRARY_PATH 环境变量指明的路径中查找。
- ③ /ect/ld.so.conf 文件中指定的搜索路径查找。
- ④ /lib、/lib64、/usr/lib、/usr/lib64 中查找。
9 库的显示调用
9.1 显示调用介绍
- 隐式调用(静态调用):将动态链接库和其它源程序文件(或者目标文件)一起参与链接。
- 显式调用(动态调用):手动调用动态链接库中包含的资源,同时用完后要手动将资源释放。
9.2 函数实现
#include <stdio.h>
#include <stdlib.h>
/*无需引入和动态链接库相关的头文件*/
#include <dlfcn.h> /*显示调用动态链接库的头文件*/
int main(void){
/*========1. 打开库文件,将库文件装载到内存中========*/
/*
void *dlopen (const char *filename, int flag);
成功返回打开的文件指针,失败返回NULL
filename:绝对路径或文件名。
文件名时依次查找:
LD_LIBRARY_PATH 环境变量指定的目录、
/etc/ld.so.cache 文件中指定的目录、
/usr/lib、/usr/lib64、/lib、/lib64 等目录
flag:
RTLD_NOW:将库文件中所有的资源都载入内存
RTLD_LAZY:暂时不降库文件中的资源载入内存,使用时才载入
*/
void*p = dlopen("libhello.so",RTLD_LAZY); /*库文件指针*/
/*===========2. 函数操作失败的错误信息==========*/
/*
const char *dlerror(void);
最近一次操作执行成功,返回NULL。
*/
if(dlerror()){
printf("error 1\n");
return 0;
}
/*===========3. 获取函数指针===============*/
/*
void *dlsym(void *handle, char *symbol);
hanle 参数表示指向已打开库文件的指针
symbol 参数用于指定目标函数的函数名
成功返回函数指针,失败返回NULL
*/
void(*fp_fn)(void) = dlsym(p, "out_hello");
if(dlerror()){
printf("error 2\n");
return 0;
}
/*============4. 调用函数=====================*/
fp_fn();
/*============5. 关闭已打开的动态链接库=============*/
/*
int dlclose (void *handle);
当函数返回 0 时,表示函数操作成功;反之,函数执行失败
*/
dlclose(p);
return 0;
}
9.3 链接文件
gcc main.c -ldl -o test