Linux C gcc的使用

Posted 熊沐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux C gcc的使用相关的知识,希望对你有一定的参考价值。

gcc的使用

更多信息见gcc

编译过程

# 预处理 demo.c -> demo.i
gcc -E demo.c -o demo.i

# 编译成汇编语言 demo.i -> demo.s
gcc -S demo.i -o demo.s

# 使用汇编器翻译成机器语言 demo.s -> demo.o
gcc -c demo.s -o demo.o # demo.o是可重定位目标文件

# 链接 demo.o [*.a] [*.so] -> demo.out
ld demo.o *.a -o demo.out

编译过程的路径处理

#include的路径搜索的顺序:

  • 如果源代码中没有指明头文件的路径,如#include "mylib.h",那么必须在编译和其他对源代码处理的环节都需要指明头文件的位置,方式是使用-I dir指明头文件所在的路径,通常一个库的头文件放置在include目录下,即使用-I xxx/include
  • 如果源代码中指明头文件的绝对或相对路径,如#include "/xxx/xxx/mylib.h"#include "../mylib.h",则不需要在后续处理中指明,但是在源代码中指明的方式增加的工程的复杂性和文件间的依赖性(这种依赖性可以通过cmake工具简化)。

gcc路径搜索

  1. 首先搜索编译时的工作路径,即.
  2. iquote dir提供的路径,c文件中的形式只能是#include "file.h"的引用形式,其他三种命令也支持使用#include<file.h>
  3. -I dir提供的路径
  4. -isystem dir提供的路径
  5. 标准库的路径
  6. -idirafter dir提供的路径

库文件的创建和使用

静态库的创建

静态库必须要求lib开头

# 制作静态库
ar rcs libxxx.a xxx.o yyy.o ..

# 查看.a文件中的函数
ar -t *.a

# 查看.a文件的反汇编代码
objdump -S libxxx.a
  • ar命令的解释:一个打包工具,将成员文件按照一定的规则构建到.a文件中
    • r替换归档文件中已有的文件或加入新文件
    • c没有库文件时创建
    • s作为ranlib工作为库中的可重定位目标文件创建符号索引或者更新

动态库的创建

# 制作动态库
gcc -shared -o libxxx.so xxx.o yyy.o ..

# 查看.so文件的反汇编代码
objdump -S libxxx.so
  • -shared产生一个共享对象(该对象具有执行权限),然后可以将其与其他对象链接以形成可执行文件。-shared

    • 在生成动态库文件时必须指明这一操作,向gcc声明生成的文件是动态可链接的,否则在链接时会出现
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o: in function `_start\':
(.text+0x24): undefined reference to `main\'
collect2: error: ld returned 1 exit status
  • 对使用-shared指令处理的文件进行执行时会出现段错误 (核心已转储)
  • -fPIC指明可重定位目标文件与位置无关,一般gcc默认开启

库文件的使用

注意对顺序的要求

  • -static强制使用静态库,如果库文件目录下有同名的静态库和动态库,gcc默认使用动态库,否则需要使用-static强制使用静态库
  • -L dir指明库文件的路径
  • -l xxx-lxxx指明需要链接的库的名称,其中库文件的名称是libxxx.alibxxx.so
  • 使用动态库时如果库不在当前工作目录下,则可能找不到,应该设置环境变量LD_LIBRARY_PATH=./lib/

注意-shared-static并不是两个相对的指令或概念,-shared表示在制作动态库时,向gcc声明生成的文件是动态可链接的,以供其他对象链接,-static表示在生成可执行文件时强制使用静态库文件。如果在生成可执行文件中添加-shared,表示该“可执行文件”可以被其他文件链接,这显然造成错误(尝试执行会出现段错误)

信息的查看

objdump

案例

项目的原始结构

.
├── bin
├── build
│   └── CMakelists.txt
├── include
│   ├── addvec.h
│   ├── mulvec.h
│   └── print.h
├── lib
├── out
└── src
    ├── addvec.c
    ├── main.c
    ├── mulvec.c
    └── print.c

main源代码使用addvec、mulvec和print文件的函数,这些函数的声明在对应的头文件下。main中的include的写法为

#include "addvec.h"
#include "mulvec.h"
#include "print.h"

编译生成可重定位目标文件

# 注意 我的gcc默认使用-fPIC进行编译,所以这里加不加都可
gcc -fPIC -o ./out/addvec.o -c ./src/addvec.c -I ./include/
gcc -fPIC -o ./out/mulvec.o -c ./src/mulvec.c -I ./include/
gcc -fPIC -o ./out/print.o -c ./src/print.c -I ./include/
gcc -fPIC -o ./out/main.o -c ./src/main.c -I ./include/

制作库

静态库

ar rcs ./lib/libmath.a ./out/addvec.o ./out/mulvec.o 
ar rcs ./lib/libprint.a ./out/print.o

动态库

gcc -shared -o ./lib/libmath.so ./out/addvec.o ./out/mulvec.o
gcc -shared -o ./lib/libprint.so ./out/print.o

链接生成可执行文件

# 使用静态库和使用静态库生成的文件
gcc -static -o ./bin/prog_static ./out/main.o -L ./lib/ -lprint -lmath
./bin/prog_static

# 使用动态库和使用动态库生成的文件
gcc -o ./bin/prog_shared ./out/main.o -L ./lib/ -lprint -lmath
LD_LIBRARY_PATH=./lib/ ./bin/prog_shared

编译后结构

.
├── bin
│   ├── prog_shared
│   └── prog_static
├── build
│   └── CMakelists.txt
├── include
│   ├── addvec.h
│   ├── mulvec.h
│   └── print.h
├── lib
│   ├── libmath.a
│   ├── libmath.so
│   ├── libprint.a
│   └── libprint.so
├── out
│   ├── addvec.o
│   ├── main.o
│   ├── mulvec.o
│   └── print.o
└── src
    ├── addvec.c
    ├── main.c
    ├── mulvec.c
    └── print.c

以上是关于Linux C gcc的使用的主要内容,如果未能解决你的问题,请参考以下文章

linux下c编程怎么编译

如何使用Linux终端(如python,在gcc或g ++中编译一行C ++代码?

Linux ——gcc/g++的使用

Linux下详解gcc编译过程(含代码示例)&& gcc使用教程

为啥使用非标准 C(gcc 特定功能)对 linux 内核进行编码? [关闭]

用C语言实现Linux命令——模拟gcc