GCC

在第二章学习gcc时,感觉还是有比较多的东西没有理解完全,在此写下博客,来接续总结归纳以下~

编写模式

-E:仅执行编译预处理 
-S:将C代码转换为汇编代码 
-c:仅执行编译操作,不进行连接操作 -o:指定生成的输出文件 -I(大写i):指定头文件目录 -l :指定程序要链接的库 -L:指定库文件所在的目录名  比如我们想要将xx.c文件编译为汇编文件,就可以` gcc -S xx.c`,编译完成后用`cat xx.c`进行查看。

一般来说,需要将.c文件经过gcc -E xx.c -o xx.i 将xx.c文件预处理为xx.i文件

再经过 gcc -S xx.i -o xx.s将预处理输出文件xx.i会变成xx.s文件

然后经过gcc -c pass.s -o pass.o输出

最后将用 gcc pass.o -o pass 将pass.o链接为可执行文件

老师所讲的ppt中有关于objdump命令的相关内容,用 man objdump手册帮助查看objdump的功能:

可见老师所给的objdump -d xx.o是disassemble的作用,即从objfile中反汇编那些特定指令机器码的section。

我之前不太明白到底什么时候gcc后跟着两个或者多个文件,什么时候只需要跟主函数所在的.c文件就行

这种情况下我们只需要执行gcc hello.c -o hello 即可
但有的时候,可能不希望把hello_f里的函数都开放使用,只允许使用一部分。那么这个时候就不能include整个.c文件了,需要使用头文件,把允许使用的函数放在头文件里。

于是我们作出修改
这里hello_f.h为

#ifndef JSONDATA_HELLO_F_H
#define JSONDATA_HELLO_F_H
void fun();
#endif //JSONDATA_HELLO_F_H

hello.c改为

#include"hello_f.h"
int main(){
    fun();
}

hello_f.c为

#include<stdio.h>
void fun(){
    printf("hi");
}
void fun2(){
    printf("f2");
}

gcc hello.c hello_f.c -o xx.c或者gcc hello.c hello_f.c来编译运行这三个文件

也可以独立进行编译
用代码

gcc -c hello.c
gcc -c hello_f.c
gcc hello.o hello_f.o

这两种方法有以下区别:

  • 第一种方法编译时需要所有文件重新编译;
  • 第二种只重新编译修改的文件,未修改的不用重新编译。

输入 gcc *.c一起编译所有的.c文件

老师的ppt中讲到.f文件一般是存放在include目录下面,而我的是在桌面,下面是用 sudo mv hello_f.c /usr/include来将.h文件移动到此目录下:

同样把hello.c和hello_f.c移动到/usr/src中去编译运行:

gcc 相关参数

gcc - 参数

-I ( i 的大写) :指定头文件路径(相对路径或绝对路径,建议相对路径)

-i :指定头文件名字 (一般不使用,而是直接放在.c 文件中通过#include<*.h> 添加)

-L:指定连接的动态库或者静态库路径(相对路径或觉得路径,建议相对路径)

-l (L的小写):指定需要链接的库的名字【注意这里是名字,不用加上lib和.a了】(链接 libc.a :-lc 链接动态库:libc.so : -lc 注意:-l后面直接添加库名省区“lib”和“.so”或“.a” )
举例:gcc -Iinclude src/*.c -o bin/hello2因为是在创建的一个文件夹目录下的,使用的是相对路径。-I后跟的就是制定头文件路径,存放在/project1/include中。
举例:gcc -o example1 example1.c -I /usr/local/include/freetype2 -lfreetype -lm
上面这句话在编译example1.c 时,-I /usr/local/include/freetype2 表示将/usr/local/include/freetype2作为第一个寻找头文件的目录,参数-l (小写的i)。lfreetype ,-l (小写的l)参数就是用来指定程序要链接的库,-l参数紧接着就是库名。指定程序链接的库名是freetype.
-lm 表示程序指定的链接库名是m (math数学库)

动态库和静态库

分类
一般库分为两种:动态库、静态库

在Linux中
如果是动态库:库文件是以.so作为后缀的,
如果是静态库:库文件是以.a作为后缀的。
在windows中
如果是动态库:库文件是以.dll作为后缀的,
如果是静态库:库文件是以.lib作为后缀的。
命名
库文件的命名:libXXX.so 或者 libYYYY.a-.
其实也就是lib加库名字,再加后缀,后缀后面可能还跟一些东西。

查看系统库文件的命名,可见此库为一动态库

去掉lib前缀,去掉.a- 或 .so-(包含后缀)之后的部分,剩下的就是库名称。举个栗子:libc-2.17.so:去掉前缀lib,去掉后缀.so,剩下的c-2.17就是库的真实名字。

为什么要制作库?
函数定义和函数实现进行分离,一是方便维护。还有一个原因是为了私密。因为有时候别人想使用我们的代码,但是我们又不能把源码发给别人,所以我们可以制作成库(库是二进制文件),然后给别人使用,这样就保证了私密性。

创建静态库

  • 创建文件夹mkdir test1
    在此文件夹中创建程序
  • 先用 gcc -c hello.c将hello.c编译成hello.o
  • ar -crv libmyhello.a hello.o创建静态库用 ar 命令。在系统提示符下键入以下命令将创建静态库文件libmyhello.a
    还可以用 ar -rc libmyhello.a hello.o来创建

在程序中使用静态库

 方法一:gcc -o hello main.c -L. -lmyhello
自定义的库时,main.c 还可放在-L.和 –lmyhello 之间,但是不能放在它俩之后,否则会提示 myhello没定义,但是是系统的库时,如 g++ -o main(-L/usr/lib) -lpthread main.cpp就不出错。-L.表示链接的库在当前目录。

 方法二: gcc main.c libmyhello.a -o hello

 方法三:先gcc -c main.c生成main.o文件,再用gcc -o hello main.o libmyhello.a一起编译链接main.o和libmyhello.a

检验
rm libmyhello.a查看静态库是否真正链接到目标文件hello中去了

创建动态库

仍旧以我们的hello.c为例子,创建动态库:gcc -shared -fPIC -o libmyhello.so hello.o

  • shared:表示指定生成动态链接库,不可省略
  • -fPIC:表示编译为位置独立的代码,不可省略
  • -o 同样不可省略

在程序中使用动态库
在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明动态库名进行编译。我们先运行 gcc 命令生成目标文件,再运行它看看结果。
使用命令 gcc -o hello main.c -L. -lmyhello

此时会报错如上图
因为虽然连接时用的是当前目录的动态库,但是运行时,是到
/usr/lib 中找库文件的,所以解决方法是将文件 libmyhello.so 复制到目录/usr/lib 中就 OK 了
使用命令 sudo mv libmyhello.so /usr/lib

静态库和动态库的比较

因为静态库和动态库的使用完全一样(gcc -o hello main.c -L. -lmyhello),那当静态库和动态库同名时,gcc 命令会使用哪个库文件呢?我们先删除除.c和.h外的所有文件,恢复成我们刚刚编辑完举例程序状态。
使用命令 sudo rm -f hello hello.o /usr/lib/libmyhello.so
随后我们接连创建静态库libmyhello.a和动态库libmyhello.so,然后用 gcc -o hello main.c -L. -lmyhello 来编译运行

当我们执行hello文件时,发现出现报错
可见当静态库和动态库同名时,gcc 命令将优先使用动态库,默认去连/usr/lib 和/lib 等目录中的动态库,将文件 libmyhello.so 复制到目录/usr/lib中即可

参考资料

https://blog.csdn.net/qq_41683305/article/details/105375214
[https://blog.csdn.net/qq_58724706/article/details/125185425](Linux 动态库和静态库)
[https://blog.csdn.net/Zx13170918986/article/details/120765657](GCC 命令行详解 -L 指定库的路径 -l 指定需连接的库名)