【Linux操作系统】GCC编译与静态库、动态库制作详解

GCC是一款广泛使用的开源编译器,它支持多种编程语言,并且具有强大的编译能力。在软件开发中,我们经常需要将代码编译成可执行文件或者库文件。本文将详细介绍GCC编译过程以及如何制作静态库和动态库。
在这里插入图片描述

文章目录

    • 一、GCC编译过程
      • 1. 预处理阶段
      • 2. 编译阶段
      • 3. 汇编阶段
      • 4. 链接阶段
    • 二、静态库制作
      • 1. 静态库制作
      • 2. 使用静态库
      • 补充:头文件对应
    • 三、动态库制作
      • 1. 动态库制作
      • 2. 使用动态库
      • 补充:动态库加载错误及解决方法
    • 四、总结
  • 补充:gcc使用技巧

一、GCC编译过程

GCC编译过程主要分为四个阶段:预处理、编译、汇编和链接。下面我们将逐一介绍每个阶段的作用。
在这里插入图片描述

1. 预处理阶段

预处理阶段主要是对源代码进行宏展开、头文件包含、条件编译等预处理操作。预处理器会根据源文件中的预处理指令,生成一个新的文件,通常以.i作为扩展名。

示例代码:

// main.c
#include <stdio.h>

#define PI 3.1415926

int main() {
    printf("PI = %f\n", PI);
    return 0;
}

预处理后的代码:

// main.i
# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "main.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
...

2. 编译阶段

编译阶段将预处理后的代码转换成汇编代码,即将高级语言代码翻译成汇编语言代码。编译器会检查语法错误、类型错误等,并生成一个汇编文件,通常以.s作为扩展名。

示例代码:

// main.i
# 1 "main.c"
...
int main() {
    printf("PI = %f\n", PI);
    return 0;
}

编译后的汇编代码:

// main.s
    .file "main.c"
    .section    .rodata
.LC0:
    .string "PI = %f\n"
    .text
.globl main
    .type   main, @function
main:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movsd   .LC0(%rip), %xmm0
    movl    $1, %eax
    movl    $.LC1, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .section    .rodata
.LC1:
    .string "PI = %f\n"
    .text
    .section    .note.GNU-stack,"",@progbits

3. 汇编阶段

汇编阶段将汇编代码转换成机器代码。汇编器会将汇编代码转换成二进制指令,生成一个目标文件,通常以.o作为扩展名。

示例代码:

// main.s
    .file "main.c"
...

汇编后的目标文件:

// main.o
...

4. 链接阶段

链接阶段将目标文件与所需的库文件进行链接,生成最终的可执行文件。链接器会解析目标文件中的符号引用,并将其与库文件中的符号定义进行匹配。

示例代码:

// main.o
...

链接后的可执行文件:

// main
...

二、静态库制作

好的,下面我将给出一个更详细的例子来说明如何制作和使用静态库。

1. 静态库制作

首先,我们需要创建两个源文件add.c和sub.c,分别实现加法和减法的功能。

add.c:

int add(int a, int b) {
    return a + b;
}

sub.c:

int sub(int a, int b) {
    return a - b;
}

然后,我们使用gcc命令将这两个源文件编译成目标文件。

$ gcc -c add.c sub.c

生成add.o和sub.o文件。

接下来,我们使用ar命令将目标文件打包成一个静态库文件libmath.a。

$ ar rcs libmath.a add.o sub.o

这一步完之后你的目录里会包含
add.c /sub.c /add.o /sub.o /libmath.a

2. 使用静态库

现在我们已经制作好了一个静态库libmath.a,接下来我们将使用这个静态库。

首先,我们创建一个main.c文件,调用静态库中的函数。

main.c:

#include <stdio.h>

extern int add(int a, int b);
extern int sub(int a, int b);

int main() {
    int a = 10;
    int b = 5;
    
    int sum = add(a, b);
    int difference = sub(a, b);
    
    printf("Sum: %d\n", sum);
    printf("Difference: %d\n", difference);
    
    return 0;
}

要进行声明函数,要不系统会默认隐形声明,容易导致错误。

然后,我们使用gcc命令将main.c文件与静态库链接起来生成可执行文件。

$ gcc main.c -L. -lmath -o main

最后,我们运行可执行文件main。

$ ./main

输出结果:

Sum: 15
Difference: 5

补充:头文件对应

假设我们有一个名为example的静态库,其中包含了一个函数void print_hello()。为了使用这个函数,我们需要有一个头文件example.h,其中包含了函数的声明。

example.h:

#ifndef EXAMPLE_H
#define EXAMPLE_H

void print_hello();

#endif

在使用这个静态库的源文件中,我们需要包含example.h头文件,并调用其中的函数。

main.c:

#include <stdio.h>
#include "example.h"

int main() {
    printf("Hello, world!\n");
    print_hello();
    return 0;
}

在编译时,我们需要指定静态库文件的搜索路径和要链接的库文件。假设libexample.a是我们的静态库文件,可以使用以下命令进行编译:

gcc -o program main.c -L/path/to/library -lexample

其中-L/path/to/library指定了静态库文件的搜索路径,-lexample指定要链接的静态库。

总结一下:首先,我们需要将多个目标文件打包成一个静态库文件,然后在编译时指定静态库的路径和名称。最后,我们可以通过调用静态库中的函数来使用其中的功能。希望这个例子能够帮助你更好地理解静态库的制作和使用过程。

三、动态库制作

动态库是在程序运行时被加载的库文件,它可以被多个程序共享使用,减少了内存的占用。

1. 动态库制作

首先,我们需要创建两个源文件add.c和sub.c,分别实现加法和减法的功能。

add.c:

int add(int a, int b) {
    return a + b;
}

sub.c:

int sub(int a, int b) {
    return a - b;
}

然后,我们使用gcc命令将这两个源文件编译成目标文件,并使用-fPIC选项生成位置无关的代码。

$ gcc -c -fPIC add.c sub.c

同样生成add.o和sub.o文件。

接下来,我们使用gcc命令将目标文件打包成一个动态库文件libmath.so。

$ gcc -shared -o libmath.so add.o sub.o

2. 使用动态库

现在我们已经制作好了一个动态库libmath.so,接下来我们将使用这个动态库。

首先,我们创建一个main.c文件,调用动态库中的函数。

main.c:

#include <stdio.h>
#include <dlfcn.h>

int main() {
    void* handle = dlopen("./libmath.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "Failed to open library: %s\n", dlerror());
        return 1;
    }

    int (*add)(int, int) = dlsym(handle, "add");
    int (*sub)(int, int) = dlsym(handle, "sub");

    int a = 10;
    int b = 5;

    int sum = add(a, b);
    int difference = sub(a, b);

    printf("Sum: %d\n", sum);
    printf("Difference: %d\n", difference);

    dlclose(handle);

    return 0;
}
/*
首先,在main函数中,我们声明了一个void指针变量handle,用于存储打开动态库后返回的句柄。然后,我们使用dlopen函数打开动态库文件libmath.so,指定RTLD_LAZY标志表示在需要时才解析符号。如果打开动态库失败,我们使用dlerror函数获取错误信息并打印到stderr流上,然后返回1表示出错。

接下来,我们使用dlsym函数获取动态库中的函数指针。dlsym函数的第一个参数是动态库的句柄,第二个参数是要获取的函数名。我们使用函数指针的方式来声明和初始化两个函数指针变量add和sub,分别指向动态库中的add函数和sub函数。

然后,我们定义了两个整型变量a和b,并分别赋值为10和5。

接下来,我们通过调用函数指针变量add和sub来调用动态库中的函数,得到加法和减法的结果,并分别赋值给sum和difference变量。

最后,我们使用dlclose函数关闭动态库句柄,释放资源。


*/

然后,我们使用gcc命令将main.c文件与动态库链接起来生成可执行文件,并指定动态库的路径和名称。

$ gcc main.c -L. -ldl -o main

最后,我们运行可执行文件main。

$ ./main

输出结果:

Sum: 15
Difference: 5

总结一下:首先,我们需要将多个目标文件编译成位置无关的代码,并使用gcc命令将它们打包成一个动态库文件。然后,在使用动态库的程序中,我们需要使用dlopen函数打开动态库,并使用dlsym函数获取动态库中的函数指针。最后,我们可以通过调用动态库中的函数来使用其中的功能。

补充:动态库加载错误及解决方法

如果动态库路径错误,可以按照以下方法解决:

  1. 检查动态库文件的路径是否正确:确保指定的路径是动态库文件所在的准确路径。
  2. 使用绝对路径或相对路径:可以使用绝对路径来指定动态库的路径,例如/path/to/library/libexample.so。或者,使用相对路径来指定动态库的路径,相对路径是相对于当前工作目录的路径,例如./libexample.so
  3. 设置LD_LIBRARY_PATH环境变量:可以通过设置LD_LIBRARY_PATH环境变量来指定动态库的搜索路径。例如,如果动态库文件在/path/to/library目录中,可以执行以下命令:
    export LD_LIBRARY_PATH=/path/to/library:$LD_LIBRARY_PATH
    
    这将把/path/to/library添加到动态库的搜索路径中。
  4. 使用rpath选项:可以在链接时使用-rpath选项来指定动态库的搜索路径。例如,使用以下命令来编译和链接程序:
    gcc -o program main.c -L/path/to/library -Wl,-rpath=/path/to/library -lexample
    
    这将在程序中设置动态库的搜索路径为/path/to/library

通过以上方法,您可以解决动态库路径错误的问题。请注意,在使用LD_LIBRARY_PATH环境变量或rpath选项时,确保指定的路径是正确的,并且动态库文件存在于该路径中。

四、总结

本文详细介绍了GCC编译过程以及如何制作静态库和动态库。通过预处理、编译、汇编和链接四个阶段,我们可以将源代码转换成可执行文件或者库文件。静态库将多个目标文件打包成一个文件,程序在编译时会将静态库的代码复制到可执行文件中;而动态库是在程序运行时被加载的库文件,它可以被多个程序共享使用,减少了内存的占用。
内容补充:


补充:gcc使用技巧

gcc是GNU Compiler Collection(GNU编译器套件)的缩写,是一个广泛使用的编程语言编译器。它支持多种编程语言,包括C、C++、Objective-C、Fortran、Ada和Go等。下面是gcc的常用参数和其作用的简要说明:

  • -c:只编译源文件,生成目标文件(.o文件),不进行链接操作。
  • -o:指定输出文件的名称。
  • -I:指定头文件的搜索路径。
  • -L:指定库文件的搜索路径。
  • -l:链接时使用的库文件。
  • -g:生成调试信息,用于调试程序。
  • -Wall:开启所有警告信息。
  • -Werror:将警告视为错误。
  • -std:指定使用的C或C++标准。
  • -O:优化级别,包括-O0(无优化)、-O1(基本优化)、-O2(更多优化)和-O3(最大优化)等。
  • -shared:生成一个共享库文件(动态库)。
  • -fPIC:生成位置无关的代码,用于生成动态库。
  • -pthread:链接多线程库。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/64857.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

龙芯积极研发二进制翻译,提升软硬件兼容性,提高LoongArch架构

根据8月8日Phoronix报道&#xff0c;龙芯正在积极研发龙芯二进制翻译功能&#xff08;Loongson Binary Translationm&#xff0c;LBT&#xff09;以提高LoongArch架构与其他处理器&#xff08;如MIPS/x86/Arm&#xff09;的二进制翻译能力&#xff0c;这重要举措将显著提升龙芯…

list交并补差集合

list交并补差集合 工具类依赖 <dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version> </dependency><dependency><groupId>commons-collections&…

数据库管理员知识图谱

初入职场的程序猿&#xff0c;需要为自己做好职业规划&#xff0c;在职场的赛道上&#xff0c;需要保持学习&#xff0c;并不断点亮自己的技能树。  成为一名DBA需要掌握什么技能呢&#xff0c;先让Chat-GPT为我们回答一下&#xff1a; 数据库管理系统 (DBMS)知识&#xff…

网络安全--mysql中事务锁以及事务隔离解析

一、事务锁 1、个人理解&#xff1a; 专一性和历史性&#xff0c;例如一个男人历史上是花心的&#xff0c;但当他成长后开启begin和update后变的专一了&#xff0c;多项事务也影响不到他了&#xff0c;直到水泥封心&#xff0c;只可被一个人查询在此我们进入正题&#xff1a;…

前端工具类

日期类 1️⃣ 新建index.js文件/*** param {date} time 需要转换的时间* param {String} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss*/ export function formatTime(time, fmt) {if (!time) {return "";}else {const date new Date(time);const o {M:…

web前端html

文章目录 快捷方式一、html5的声明二、html5基本骨架 2.1 html标签 2.2 head标签 2.3 body和head同级 2.4 body标签 2.5 title标签 2.6 meta标签 三、标题标签介绍与应用 3.1 标题的介绍 3.2 标题标签位置摆放 3.3 标签之段落、换行、水平线 3.3 标签之图片 3.3.1 图…

2023,谁在引领实时互动进入高清时代?

实践是检验真理的唯一标准&#xff0c;技术是行业进步的核心动能。在实时互动的新时代里&#xff0c;不断进化的声网已然完成自证。 作者|斗斗 出品|产业家 “一个医疗行业的客户&#xff0c;曾向我们提出一个需求&#xff0c;希望在120急救场景下&#xff0c;可以远程看清…

【C++】常用到的“using namespace std;”到底是什么?

一、引言 在初学C时&#xff0c;在包含完头文件之后&#xff0c;我们常常会看到这么一句话&#xff1a;using namespace std; 比如&#xff1a; #include<iostream> using namespace std; int main() {cout << "hello world" << endl;return 0…

dubbo之基础知识

Dubbo 官网地址&#xff1a;Apache Dubbo Dubbo 是一款易用、高性能的 WEB 和 RPC 框架&#xff0c;同时为构建企业级微服务提供服务发现、流量治理、可观测、认证鉴权等能力、工具与最佳实践 作用 1.远程方法调用 2.容错和负载均衡 3.提供服务的自动注册与发现 为什么需要…

代理模式(C++)

定义 为其他对象提供一种代理以控制(隔离&#xff0c;使用接口)对这个对象的访问。。 应用场景 在面向对象系统中&#xff0c;有些对象由于某种原因(比如对象创建的开销很大&#xff0c;或者某些操作需要安全控制&#xff0c;或者需要进程外的访问等)直接访问会给使用者、或…

接口测试——电商网站接口测试实战(四)

1. 接口测试需求分析 常见接口文档提供的两种方式 ①word文档 ②在线文档 电商网站网址模拟练习&#xff1a;Swagger UI 2. 登陆的分析 慕慕生鲜网址&#xff1a;慕慕生鲜账号密码点击execute后 输入账号密码后点击开发者工具&#xff0c;再登录&#xff0c;点击网络&…

Nginx的优化和防盗链

一、Nginx的优化 1、隐藏版本号 curl -I http://192.168.79.28 #查看信息&#xff08;版本号等&#xff09;方法一&#xff1a;修改配置文件 vim /usr/local/nginx/conf/nginx.conf vim /usr/local/nginx/conf/nginx.conf http {include mime.types;default_type ap…

2023-08-04 Untiy进阶 C#知识补充4——C#5主要功能与语法

文章目录 一、概述二、回顾——线程三、线程池四、Task 任务类五、同步和异步 ​ 注意&#xff1a;在此仅提及 Unity 开发中会用到的一些功能和特性&#xff0c;对于不适合在 Unity 中使用的内容会忽略。 一、概述 C# 5 调用方信息特性&#xff08;C# 进阶内容&#xff09;异步…

【大数据】Flink 详解(二):核心篇 Ⅰ

Flink 详解&#xff08;二&#xff09;&#xff1a;核心篇 Ⅰ 14、Flink 的四大基石是什么&#xff1f; ​ Flink 的四大基石分别是&#xff1a; Checkpoint&#xff08;检查点&#xff09;State&#xff08;状态&#xff09;Time&#xff08;时间&#xff09;Window&#xff…

54款宝藏级AIGC工具分享(claude,Midjourney,Stable Diffusion等)

随着ChatGPT的一波又一波高潮&#xff0c;生成式AI逐渐进入人们视野&#xff0c;并开始大行其道&#xff0c;正如人们所说&#xff1a;AI用的好&#xff0c;天天下班早&#xff01; 当然&#xff0c;有效的利用AI不但能下班早&#xff0c;还能在上班时间摸鱼&#xff0c;就如潘…

【Docker】数据库动态授权组件在Kubernetes集群下的测试过程记录

目录 背景 组件原理 测试设计 环境 测试脚本 脚本build为linux可执行文件 镜像构建 Dockerfile Docker build 镜像有效性验证 总结 资料获取方法 背景 我们都知道出于安全性考虑&#xff0c;生产环境的权限一般都是要做最小化控制&#xff0c;尤其是数据库的操作授…

2023年华数杯数学建模B题思路代码分析 - 不透明制品最优配色方案设计

# 1 赛题 B 题 不透明制品最优配色方案设计 日常生活中五彩缤纷的不透明有色制品是由着色剂染色而成。因此&#xff0c;不透明 制品的配色对其外观美观度和市场竞争力起着重要作用。然而&#xff0c;传统的人工配色 存在一定的局限性&#xff0c;如主观性强、效率低下等。因此…

华三H3C S5120V3交换机的配置之组建IRF

IRF&#xff08;Intelligent Resilient Framework&#xff0c;智能弹性架构&#xff09;&#xff0c;是华三交换机实现虚拟堆叠的一种技术&#xff0c;其核心思想是将多台交换机连接在一起&#xff0c;虚拟成一台交换机&#xff0c;进而实现统一管理。和传统的堆叠概念不同&…

Python如何解决Amazon亚马逊“图文验证码”识别(6)

前言 本文是该专栏的第55篇,后面会持续分享python爬虫干货知识,记得关注。 在本专栏前面,笔者有详细介绍多种登录验证码识别方法,感兴趣的同学可往前翻阅。而本文,笔者将单独详细介绍亚马逊Amazon的图文识别验证码的解决方法。 如上图所示,访问或请求频次达到一定程度之…

笔记本WIFI连接无网络【实测有效,不用重启电脑】

笔记本Wifi连接无网络实测有效解决方案 问题描述&#xff1a; 笔记本买来一段时间后&#xff0c;WIFI网络连接开机一段时间还正常连接&#xff0c;但是过一段时间显示网络连接不上&#xff0c;重启电脑太麻烦&#xff0c;选择编写重启网络脚本解决。三步解决问题。 解决方案&a…