【GCC/G++】gcc/g++入门

GCC/G++ 编译过程

在这里插入图片描述

示例代码

head.h

#ifndef __HEAD_H__
#define __HEAD_H__

#define NUM1 10
#define NUM2 20
#endif

sum.c

#include <stdio.h>
#include "head.h"
//#define DEBUG

int main(void)
{
    int a = NUM1;
    int aa;
    int b = NUM2;
    int sum = a + b;
    // 小盆友: 这是一个加法运算
#ifdef DEBUG
    printf("The sum value is: %d + %d = %d\n", a, b, sum);
#endif
    return 0;
}

GCC/G++ 分步骤编译过程演示

# ls
head.h  sum.c

//  预处理器(头文件复制到当前目录下,宏替换等操作)
# gcc -E sum.c -o sum.i

# ls
head.h  sum.c  sum.i

//  编译器(C/C++代码变成汇编代码)
# gcc -S sum.i -o sum.s

# ls
head.h  sum.c  sum.i  sum.s

//  汇编器(把汇编代码变成二进制)
# gcc -c sum.s -o sum.o

# ls
head.h  sum.c  sum.i  sum.o  sum.s

//  链接器(链接其他需要的二进制代码成最后的可执行二进制代码)
# gcc sum.o -o sum

# ls
head.h  sum  sum.c  sum.i  sum.o  sum.s

GCC/G++ 直接编译成可执行程序演示

## -o 是命名的意思
# gcc sum.c -o sumc

GCC/G++ 命令参数的常用选项

选项解释
-ansi只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色, 例如 asm 或 typeof 关键词。
-c只编译并生成目标文件。
-DMACRO以字符串"1"定义 MACRO 宏。
-DMACRO=DEFN以字符串"DEFN"定义 MACRO 宏。
-E只运行 C 预编译器。
-g生成调试信息。GNU 调试器可利用该信息。
-IDIRECTORY指定额外的头文件搜索路径DIRECTORY。
-LDIRECTORY指定额外的函数库搜索路径DIRECTORY。
-lLIBRARY连接时搜索指定的函数库LIBRARY。
-m486针对 486 进行代码优化。
-o FILE生成指定的输出文件。用在生成可执行文件时。
-O0不进行优化处理。
-O 或 -O1优化生成代码。
-O3比 -O2 更进一步优化,包括 inline 函数。
-shared生成共享目标文件。通常用在建立共享库时。
-static禁止使用共享连接。
-UMACRO取消对 MACRO 宏的定义。
-w不生成任何警告信息。
-Wall生成所有警告信息。
-Wl,-rpath=<your_lib_dir>指定编译好的程序在运行时动态库的目录(可以 man gcc 搜索 -Wl查看),当编译好程序后用 ldd 就可以看到你指定的路径了。

GCC/G++ 一些参数详解

如果头文件和目标文件不在同一目录下

# ls
head.h  sum.c

# mkdir include

# ls
head.h  include  sum.c

# mv head.h include/.

# ls
include  sum.c

这时候编译就会出现报错:

# gcc sum.o -o sum
sum.c:2:10: fatal error: head.h: No such file or directory
    2 | #include "head.h"
      |          ^~~~~~~~
compilation terminated.

原因是include “” 只会查找当前执行文件所在的目录的文件,所以无法找到

#include "head.h"

指定头文件目录

//  -I 指定头文件目录
# gcc -o sum sum.c -I./include

指定宏

# gcc -o sum sum.c -I./include
# ./sum

//  -D 指定宏
# gcc -o sum sum.c -I./include -D DEBUG
# ./sum
The sum value is: 10 + 20 = 30

指定优化等级

//  -On 指定优化等级,n代表数字,越大表示优化越好
# gcc -o sum sum.c -I./include -D DEBUG -O3

警告提示

//  -Wall 会给代码中一些不好的地方提出警告
# gcc -o sum sum.c -I./include -D DEBUG -O3 -Wall

加入gdb调试编译信息

//  -g 表示加入gbd调试编译信息,然后就可以gdb进行调试
# gcc -o sum sum.c -I./include -D DEBUG -O3 -g

//  gdb调试
# gbd ./sum

GCC/G++ 静态库制作和使用

命名规则
  1. lib开头
  2. 静态库名
  3. .a结尾

如:libsrot.a

优缺点及使用场景

优点

  1. 寻址方便,速度快
  2. 库被打包到可执行程序中,直接发布可执行程序即可使用,不需要发布.a文件出去了。

缺点

  1. 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。
  2. 如果静态函数库改变了,那么你的程序必须重新编译。

使用场景

  1. 在核心程序上使用,保证速度,可忽视空间
  2. 主流应用于80、90年代,现在很少用
静态库制作
  1. 生成对应的.o文件
  2. 将生成的.o文件打包成.a文件(使用工具ar)

示例

// include目录存放头文件 src目录存放源文件 lib目录存放静态库文件
# ls 
include  lib  main.c  src 

// 目标:把src目录下的所有源文件制作成一个静态库文件存放到lib目录下

# cd src
# ls
add.c  div.c  mul.c  sub.c

// 把当前目录下所有.c文件生成为.o文件
# gcc *.c -c -I../include

# ls
add.c  add.o  div.c  div.o  mul.c  mul.o  sub.c  sub.o

// 使用ar工具打包所有.o文件到静态库文件(这里我指定的是libMyCalc.a)
# ar rcs libMyCalc.a *.o

# ls
add.c  add.o  div.c  div.o  libMyCalc.a  mul.c  mul.o  sub.c  sub.o

// 查看库中的符号(函数、全局变量等)
# nm libMyCalc.a
add.o:
0000000000000000 T add
div.o:
0000000000000000 T div
mul.o:
0000000000000000 T mul
sub.o:
0000000000000000 T sub

# mv libMyCalc.a ../lib/

# ls
add.c  add.o  div.c  div.o  mul.c  mul.o  sub.c  sub.o

# cd ../lib/

# ls 
libMyCalc.a
发布静态库
  1. 发布静态库(.a)
  2. 头文件(.h)

示例

// include目录存放头文件 src目录存放源文件 lib目录存放静态库文件
# ls 
include  lib  main.c  src 

// 编译方法一:直接指定静态库和头文件目录
# gcc -o sum main.c lib/libMyCalc.a -I./include/

// 编译方法二:-L指定库目录 -l指定库名(静态库的库名是去掉lib和.a)-I指定头文件目录
# gcc main.c -L./lib -lMyCalc -I./include -o sum

# ls 
include  lib  main.c  src  sum

# ./sum
sum = 26

// 查看可执行文件中的符号(函数、全局变量等)
# nm sum

// 查看文件格式
# file app
app: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), BuildID[sha1]=ff4f7b88f3a25310b65e8cbc18ad297f9fb37943, for GNU/Linux 2.6.32, not stripped

// 查看文件格式
# file main.c
main.c: C source, ASCII text

GCC/G++ 动态库制作和使用

命名规则
  1. lib开头
  2. 动态库名
  3. .so结尾

如:libsrot.so

优缺点及使用场景

优点

  1. 节省内存(共享)
  2. 易于更新(动态链接):停止运行程序–>使用新库覆盖旧库(保证新旧库名称一致,接口一致) “接口”–>重新启动程序。

缺点

延时绑定,速度略慢

使用场合

对速度要求不是很强烈的地方都应使用动态库

注意事项

动态库是否加载到内存,取决于程序是否运行。

制作动态库
  1. 生成与位置无关的代码
  2. 将.o打包成共享库(.so)

示例

// include目录存放头文件 src目录存放源文件 lib目录存放动态库文件
# ls 
include  lib  main.c  src 

// 目标:把src目录下的所有源文件制作成一个动态库文件存放到lib目录下

# cd src
# ls
add.c  div.c  mul.c  sub.c

// 把当前目录下所有.c文件生成为.o文件
// 参数 -fPIC 表示生成与位置无关代码
# gcc -fPIC *.c -c -I../include

# ls
add.c  add.o  div.c  div.o  mul.c  mul.o  sub.c  sub.o

// 参数:-shared 制作动态库
# gcc -shared -o libMyCalc.so *.o -I../include

# ls
add.c  add.o  div.c  div.o  libMyCalc.so  mul.c  mul.o  sub.c  sub.o

// 查看库中的符号(函数、全局变量等)
# nm libMyCalc.so
0000000000001105 T add
0000000000004028 b completed.7326
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000001050 t deregister_tm_clones
000000000000111f T div
00000000000010c0 t __do_global_dtors_aux
0000000000003e10 t __do_global_dtors_aux_fini_array_entry
0000000000003e18 d __dso_handle
0000000000003e20 d _DYNAMIC
000000000000116c T _fini
0000000000001100 t frame_dummy
0000000000003e08 t __frame_dummy_init_array_entry
00000000000020f8 r __FRAME_END__
0000000000004000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000002000 r __GNU_EH_FRAME_HDR
0000000000001000 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000001138 T mul
0000000000001080 t register_tm_clones
0000000000001151 T sub
0000000000004028 d __TMC_END__


# mv libMyCalc.so ../lib/

# ls
add.c  add.o  div.c  div.o  mul.c  mul.o  sub.c  sub.o

# cd ../lib/

# ls 
libMyCalc.so
发布动态库
  1. 发布动态库(.so)
  2. 头文件(.h)

示例

// include目录存放头文件 src目录存放源文件 lib目录存放静态库文件
# ls 
include  lib  main.c  src 

// 编译方法一:直接指定动态库和头文件目录
# gcc main.c -o sum lib/libMyCalc.so -I./include

// 编译方法二:-L指定库目录 -l指定库名(动态库的库名是去掉lib和.a)-I指定头文件目录
# gcc main.c -o sum -L./lib -lMyCalc -I./include

# ls 
include  lib  main.c  src  sum

# ./sum
./sum: error while loading shared libraries: libMyCalc.so: cannot open shared object file: No such file or directory

// 查看可执行文件中所有依赖的共享库(ldd)
// 可以看到动态库没有找到
// 可执行程序运行时,需要加载动态库,加载动态库由动态链接器完成,ldd打印的最后一行就是动态链接器(即如下/lib64/ld-linux-x86-64.so.2 (0x00007fa297e9d000)),注意动态链接器本身也是一个动态库
// 动态链接器链接动态库通过环境变量查找动态库路径
# ldd sum
	linux-vdso.so.1 =>  (0x00007ffeae49e000)
	libMyCalc.so => not found
	libc.so.6 => /lib64/libc.so.6 (0x00007fa297acf000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fa297e9d000)

// 打印环境变量
# echo $PATH
/opt/rh/devtoolset-9/root/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/nginx/sbin:/usr/local/libxml2/bin:/root/bin

// 设置动态库路径,使加载器知道到哪里加载我们的动态库

// 方法一:把动态库放到/lib中(放到系统库目录中)。不推荐
# cp lib/MyCalc.so /lib

// 方法二:把动态库路径加入到LD_LIBRARY_PATH字段。不过该方法只是临时的,关闭终端后就会失效
# export LD_LIBRARY_PATH=./lib

// 方法三:永久设置。原理是:家目录的.bashrc文件每次终端启动都会加载一次。不常用。
# cd //进入家目录
# vi .basnrc //在最后一行添加 export LD_LIBRARY_PATH=/home/ronghui/test/build_lib_a_test/lib/(需要添加的动态库的绝对路径)
 
 // 方法四:永久设置。
# cd /etc/
# sudo vi ld.so.conf //在最后一行添加/home/ronghui/test/build_lib_a_test/lib/(需要添加的动态库的绝对路径)
# ldconfig -v //重新加载配置

// 方法五:gcc/g++添加运行时加载动态库路径
// 在gcc/g++最后加上-Wl,-rpath=<your_lib_dir> 指定编译好的程序在运行时动态库的目录(可以 man gcc 搜索 -Wl查看)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值