【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.1415926int 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   %rbpmovq    %rsp, %rbpsubq    $16, %rspmovsd   .LC0(%rip), %xmm0movl    $1, %eaxmovl    $.LC1, %edimovl    $0, %eaxcall    printfmovl    $0, %eaxleaveret.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_Hvoid 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:链接多线程库。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

打破传统直播,最新数字化升级3DVR全景直播

导语&#xff1a; 近年来&#xff0c;随着科技的不断创新和发展&#xff0c;传媒领域也正经历着一场前所未有的变革。在这个数字化时代&#xff0c;直播已经不再仅仅是在屏幕上看到一些人的视频&#xff0c;而是将观众带入一个真实世界的全新体验。其中&#xff0c;3DVR全景直…

架构训练营学习笔记:6-1 微服务

序 这部分是了解的。传统企业使用soa较多。很多企业银行、电信对于Oracle 依赖大&#xff0c;强调稳定性。各个项目侧重外包&#xff0c;技术栈不统一。 soa 历史 这个之前电信的BOSS系统就是这种架构&#xff0c;不知道现在呢&#xff0c;核心计费系统billing是运行在tuxduo…

【Linux】操作系统与冯诺依曼体系——深度解析(软硬件层面)

​ 前言 大家好吖&#xff0c;欢迎来到 YY 滴 Linux系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过Linux的老铁&#xff0c;从软硬件层面向大家介绍操作系统与冯诺依曼体系&#xff0c; 主要内容含&#xff1a; 欢迎订阅 YY滴Linux专栏&#xff01;更多干货持…

el-table那些事

el-table那些事 获取el-table所有勾选的行数据 用于记录工作和日常学习遇到的坑&#xff0c;需求。 vue3element-plusts 获取el-table所有勾选的行数据 1、需要先声明一个ref变量&#xff0c;并赋值给el-table 2、通过el-table提供的getSelectionRows()函数获取选中的"行…

【iPhone】手机还有容量,拍视频却提示 iPhone 储存空间已满

文章目录 前言解决方案 结语 前言 今天在用 iPhone 录像的时候突然提醒我 iPhone储存空间已满 你没有足够的储存空间来录制视频” 可我明明还有 20G 的容量 我非常疑惑&#xff0c;因为我之前还剩1个G都能录像&#xff0c;现在20G反而不行了&#xff0c;于是重启了手机&#…

伦敦金费用有哪几方面?

通常在网上开设伦敦金投资账户是没有成本的&#xff0c;而它交易的费用&#xff0c;主要是由点差和过夜利息&#xff08;仓息&#xff09;构成。如果伦敦金投资者只是做短线的日内交易&#xff0c;做一手完整的100盎司的标准合约&#xff0c;需要支付大约50美元点差费用&#x…

Cesium 迁徙图,飞线、动态圆。

效果图放前面 符合你预期效果往下看&#xff0c;不符合出门右转&#xff0c;不耽搁大家时间。 流光线 这个流光线网上其实都有很多案例 第一步绘制抛物线 里面有些配置参数已经添加了说明。 //抛物线绘制 function parabola(twoPoints: number[]) {let s: number[] []let …

Android 13 Hotseat定制化修改——001 hotseat布局方向

目录 一.背景 二.hotseat布局方向 一.背景 由于需求是需要自定义修改Hotseat,所以此篇文章是记录如何自定义修改hotseat的,应该可以覆盖大部分场景,修改点有修改hotseat布局方向,hotseat图标数量,hotseat图标大小,hotseat布局位置,hotseat图标禁止形成文件夹,hotseat图…

7.6 创建对象内存分析

7.6 创建对象内存分析 主程序实例 package com.baidu.www.oop;import com.baidu.www.oop.demo03.Pet;public class Application {public static void main(String[] args) {Pet dog new Pet();dog.name "旺财";//这里的对象的属性在类中需要定义为public&#xff…

mongodb-win32-x86_64-2008plus-3.4.24-signed.msi

Microsoft Windows [版本 6.1.7601] 版权所有 (c) 2009 Microsoft Corporation。保留所有权利。C:\Users\Administrator>cd C:\MongoDB\Server\3.4\binC:\MongoDB\Server\3.4\bin>C:\MongoDB\Server\3.4\bin>mongod --help Options:General options:-h [ --help ] …

【C# 基础精讲】条件语句:if、else、switch

条件语句是C#编程中用于根据条件执行不同代码块的关键结构。C#支持if、else和switch三种常见的条件语句&#xff0c;它们允许根据表达式的结果决定程序的执行路径。在本文中&#xff0c;我们将详细介绍这三种条件语句的语法和使用方法。 if语句 if语句用于在给定条件为真&…

Linux Shell 编程入门

从程序员的角度来看&#xff0c; Shell本身是一种用C语言编写的程序&#xff0c;从用户的角度来看&#xff0c;Shell是用户与Linux操作系统沟通的桥梁。用户既可以输入命令执行&#xff0c;又可以利用 Shell脚本编程&#xff0c;完成更加复杂的操作。在Linux GUI日益完善的今天…

嵌入式虚拟仿真实验教学平台之串口发送数据

嵌入式虚拟仿真实验教学平台课程系列 串口发送数据实验 课程内容 本实验使用 STM32 的串口发送数据。开始仿真后,打开串口监视器&#xff0c;串口监视器会打印出要发送的数据。 课程目标 学习配置使用GPIO功能学习配置使用复用功能学习配置使用UART功能 硬件设计 本课程…

微信小程序如何配置并使用less?

1&#xff0c;检查微信开发者工具&#xff08;工具版本1.03&#xff09;————这步很重要不然后面按步骤实行后会发现急死你也还是不管用&#xff0c;我之前死在过这一步&#xff0c;所以大家不要再次踩坑了 ~ ~ 。。。 2&#xff0c;在VScode中下载Less插件 3&#xff0c;…

uni-app:分页实现多选功能

效果 代码解析 一、标签-列表 <view class"item_all" v-for"(item, index) in info" :key"index"><view class"position parameter-info text-over" :class"{checked_parameter: item.checked}" :data-id"i…

代码随想录算法训练营day59

文章目录 Day59 下一个更大元素II题目思路代码 接雨水题目思路代码 Day59 下一个更大元素II 503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 题目 给定一个循环数组&#xff08;最后一个元素的下一个元素是数组的第一个元素&#xff09;&#xff0c;输出每…

ChatGPT“侵入”校园,教学评价体制受冲击,需作出调整

北密歇根大学的教授奥曼在学生作业中发现了一篇关于世界宗教的“完美论文”。“这篇文章写得比大多数学生都要好......好到不符合我对学生的预期&#xff01;”他去问ChatGPT&#xff1a;“这是你写的吗&#xff1f;”ChatGPT回答&#xff1a;“99.9%的概率是的。” ChatGPT“侵…

【Axure 教程】动态面板

【动态面板】是 Axure 中另外一个神级的元件&#xff0c;它的江湖地位可以说跟【中继器】不相上下&#xff0c;【动态面板】提供了简单的配置&#xff0c;却可以实现非常丰富的效果&#xff0c;在实际设计中应用非常广泛。 对于刚入门的产品经理来说&#xff0c;学习【动态面板…

Bootload U-Boot分析

Bootloader是在操作系统运行之前执行的一段小程序。通过这段小程序可以初始化硬件设备、建立内存空间的映射表&#xff0c;从而建立适当的系统软硬件环境&#xff0c;为最终调用操作系统内核做好准备。 对于嵌入式系统&#xff0c;Bootloader是基于特定硬件平台来实现的。因此…

C++11之右值引用

C11之右值引用 传统的C语法中就有引用的语法&#xff0c;而C11中新增了的 右值引用&#xff08;rvalue reference&#xff09;语法特性&#xff0c;所以从现在开始我们之前学习的引用就叫做左值引用&#xff08;lvalue reference&#xff09;。无论左值引用还是右值引用&#…