gcc生成静态库和动态库、gcc背后的故事

gcc生成静态库和动态库、gcc背后的故事

一、gcc生成静态库和动态库

1.阅读、理解和学习材料“用gcc生成静态库和动态库”和“静态库.a与.so库文件的生成与使用”
(1)编辑生成例子程序hello.h、hello.c、main.c。


在这里插入图片描述
在这里插入图片描述
(2)将 hello.c 编译成.o 文件
在终端输入gcc -c hello.c即可生成所需的hello.o文件,再次在终端输入ls即可查看现有的文件。
在这里插入图片描述
此时,终端显示我们已经生成了hello.o文件。
(3)由.o 文件创建静态库
这一步需要在终端输入ar -crv libmyhello.a hello.o,这一步是为了创建相关的静态库,此时的静态库名为myhello,文件名为libmyhello.a。同样的我们可以输入ls来验证是否生成。
在这里插入图片描述

(4)在程序中使用静态库
我这里使用的是gcc -o hello main.c -L. -lmyhello
在这里插入图片描述
然后用./hello运行,我这里图片找不到了,后面找了一张图,就单输出了。
在这里插入图片描述
(5)由.o文件创建动态库文件。
在终端输入:gcc -shared -fPIC -o libmyhello.so hello.o。即可生成动态库文件,同样的我们输入ls查看是否生成。
在这里插入图片描述
(6)在程序中使用动态库
这一步浪费了很多时间,先上图,后面再解释。
在这里插入图片描述

这里在一开始的时候都还是按照课本进行,但是到了mv libmyhello.so /usr/lib时出了问题,一直显示我的权限不够,然后我用sudo mv libmyhello.so /usr/lib/libmyhello.so,进行管理员运行,这才算是解决了这个问题,./hello完成了输出。

二、编写程序并生成静态库和动态库

(1)创建文件sub1.c、sub2.c、sub.h、main1.c,文件内容如下图所示:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)使用gcc命令对文件进行编译。
在这里插入图片描述
(3)输入ar工具生成静态库文件
在这里插入图片描述
用gcc将 main函数的目标文件与此静态库文件进行链接
之后 输入./count 便可输出。
在这里插入图片描述
此时在终端输入ll查看文件大小。
(4)使用ar工具生成动态库文件
在这里插入图片描述
然后完成输出
在这里插入图片描述
输入ll libsub.a查看文件大小,并与静态库文件大小做对比,得出结论静态库生成的文件小于动态库生成的文件。

三、gcc编译器背后的故事

1、GCC:
GCC(GNU C Compiler)是编译工具。本文所要介绍的将 C/C++语言编写的程序
转换成为处理器能够执行的二进制代码的过程即由编译器完成。
2、 Binutils:
一组二进制程序处理工具,包括:addr2line、ar、objcopy、objdump、as、ld、
ldd、readelf、 size 等。这 一组工具 是开发和 调试不可 缺少的工具 ,分别简 介
如下:
(1) addr2line:用 来将程序 地址转 换成其所 对应的程 序源文 件及所对 应的代 码
行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对
应的源代码位置。
(2) as:主要用于汇编,有关汇编的详细介绍请参见后文。
(3) ld:主要用于链接,有关链接的详细介绍请参见后文。
(4) ar:主要用于创建静态库。
(5) ldd:可以用于查看一个可执行程序依赖的共享库。
(6) objcopy:将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或
者将.elf 转换成.bin 等。
(7) objdump:主要的作用是反汇编。有关反汇编的详细介绍,请参见后文。
(8) readelf:显示有关 ELF 文件的信息,请参见后文了解更多信息。
(9) size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小
等,请参见后文了解使用 size 的具体使用实例。
3、 C 运行库
C 语言标准主要由两部分组成:一部分描述 C 的语法,另一部分描述 C 标准库。
C 标准库定义了一组标准头文件,每个头文件中包含一些相关的函数、变量、类
型声明和宏定义,譬如常见的 printf 函数便是一个 C 标准库函数,其原型定义
在 stdio 头文件中。
C 语言标准仅仅定义了 C 标准库函数原型,并没有提供实现。因此,C 语言编译
器通常需要一个 C 运行时库(C Run Time Libray,CRT)的支持。C 运行时库又
常简称为 C 运行库。与 C 语言类似,C++也定义了自己的标准,同时提供相关支
持库,称为 C++运行时库
(1)准备工作
准备一个简单的程序,如图
在这里插入图片描述
(2)编译过程
2、预处理
预处理的过程主要包括以下过程:
(1) 将所有的#define 删除,并且展开所有的宏定义,并且处理所有的条件预编
译指令,比如#if #ifdef #elif #else #endif 等。
(2) 处理#include 预编译指令,将被包含的文件插入到该预编译指令的位置。
(3) 删除所有注释“//”和“/* */”。
(4) 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
(5) 保留所有的#pragma 编译器指令,后续编译过程需要使用它们。
使用gcc进行预处理并完成编译。
在这里插入图片描述
(3)汇编
汇编过程调用对汇编代码进行处理,生成处理器能识别的指令,保存在后缀为.o
的目标文件中。由于每一个汇编语句几乎都对应一条处理器指令,因此,汇编相
对于编译过程比较简单,通过调用 Binutils 中的汇编器 as 根据汇编指令和处理
器指令的对照表一一翻译即可。
当程序由多个源代码文件构成时,每个文件都要先完成汇编工作,生成.o 目标
文件后,才能进入下一步的链接工作。注意:目标文件已经是最终程序的某一部
分了,但是在链接之前还不能执行。
同样是使用gcc命令完成
在这里插入图片描述
(4)链接
链接也分为静态链接和动态链接,其要点如下:
(1) 静态链接是指在编译阶段直接把静态库加入到可执行文件中去,这样可执行
文件会比较大。链接器将函数的代码从其所在地(不同的目标文件或静态链
接库中)拷贝到最终的可执行程序中。为创建可执行文件,链接器必须要完
成的主要任务是:符号解析(把目标文件中符号的定义和引用联系起来)和
重定位(把符号定义和内存地址对应起来然后修改所有对符号的引用)。
(2) 动态链接则是指链接阶段仅仅只加入一些描述信息,而程序执行时再从系统
中把相应动态库加载到内存中去。
 在 Linux 系 统中,gcc 编 译链 接时 的动 态库 搜索 路径 的 顺序 通常 为:首 先从 gcc 命 令的
参 数-L 指 定的 路径 寻找 ;再 从环 境变 量 LIBRARY_PATH 指 定的 路径 寻址;再 从默 认路 径
/lib、/usr/lib、 /usr/local/lib 寻找 。
 在 Linux 系 统中,执 行二 进制 文件 时的 动态 库搜 索路 径的 顺序 通常 为:首 先搜 索编 译目
标 代码 时指 定的 动态 库搜 索路 径;再 从环 境变 量 LD_LIBRARY_PATH 指 定的 路径 寻址;再
从 配置 文件/etc/ld.so.conf 中 指定 的动 态库 搜索 路径 ;再 从默 认路 径/lib、/usr/lib
寻找 。
 在 Linux 系统 中, 可以 用 ldd 命令 查看 一个 可执 行程 序依 赖的 共享 库。
输入gcc hello.c -o hello
在这里插入图片描述
此时可以输入 size hello //使用 size 查看大小和 ldd hello //可以看出该可执行文件链接了很多其他动态库,主要是 Linux 的 glibc动态库
2、分析ELF文件
1.ELF 文件的段
ELF 文件格式如下图所示,位于 ELF Header 和 Section Header Table 之间的都
是段(Section)。一个典型的 ELF 文件包含下面几个段:
.text:已编译程序的指令代码段。
.rodata:ro 代表 read only,即只读数据(譬如常数 const)。
.data:已初始化的 C 程序全局变量和静态局部变量。
.bss:未初始化的 C 程序全局变量和静态局部变量。
.debug:调试符号表,调试器用此段的信息帮助调试。
可以使用 readelf -S 查看其各个 section 的信息如下在这里插入图片描述
2.反汇编 ELF
由于 ELF 文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF 文件包
含的指令和数据,需要使用反汇编的方法。
使用 objdump -D 对其进行反汇编如下:
在这里插入图片描述

四、全局常量、全局变量、局部变量、静态变量、堆、栈等,在Ubuntu(x86)系统和STM32(Keil)中的理解

1、归纳出Ubuntu、stm32下的C程序中堆、栈、全局、局部等变量的分配地址,进行对比分析

在Ubuntu和stm32下的C程序中,变量的分配地址有一些区别。下面是对比分析它们在堆、栈、全局和局部中的分配地址。

堆:

Ubuntu:在Ubuntu中,堆是由操作系统动态分配和管理的,使用malloc()或者new关键字来分配堆内存。分配的地址是从低地址到高地址增长的。
stm32:在stm32中,堆的分配通常由编译器和链接器处理。使用malloc()函数来分配堆内存。分配的地址是从高地址到低地址增长的。
栈:

Ubuntu:在Ubuntu中,栈是由操作系统自动分配和管理的,用于存储函数的局部变量和函数调用的上下文信息。栈的分配和释放是自动进行的,不需要手动管理。分配的地址是从高地址到低地址增长的。
stm32:在stm32中,栈的分配是由编译器和链接器处理的。栈的大小和分配地址由链接器脚本指定。分配的地址是从高地址到低地址增长的。
全局变量:

Ubuntu:在Ubuntu中,全局变量存储在程序的数据段中,其地址是固定的。全局变量在程序启动时被分配,并且在整个程序执行期间都存在。
stm32:在stm32中,全局变量也存储在程序的数据段中,其地址是固定的。全局变量在程序启动时被分配,并且在整个程序执行期间都存在。
局部变量:

Ubuntu:在Ubuntu中,局部变量通常存储在栈上。每次函数调用时,局部变量都会被分配到栈上,并在函数返回时自动释放。
stm32:在stm32中,局部变量通常存储在栈上。每次函数调用时,局部变量都会被分配到栈上,并在函数返回时自动释放。
总结:在Ubuntu和stm32中,堆、栈、全局和局部变量的分配地址有一些差异。堆的分配地址在Ubuntu中是从低地址到高地址增长,而在stm32中是从高地址到低地址增长。栈的分配地址在两者中都是从高地址到低地址增长。全局变量的分配地址在两者中都是固定的。局部变量在两者中都存储在栈上,并在函数返回时自动释放。

2、对ARM Cortex-M/stm32F10x的存储器地址映射的理解

ARM Cortex-M是一种32位的嵌入式处理器架构,而stm32F10x是一系列基于ARM Cortex-M内核的微控制器产品。存储器地址映射指的是将不同类型的存储器(如片上闪存、RAM、外设寄存器等)分配到处理器的地址空间中的过程。

在ARM Cortex-M架构中,存储器地址映射通常分为两个部分:内核地址空间和外设地址空间。

内核地址空间是处理器核心用于访问指令和数据的存储器区域。在ARM Cortex-M中,内核地址空间通常被分为代码存储器区域和数据存储器区域。代码存储器区域用于存放程序的指令,而数据存储器区域用于存放程序的数据。这些存储器区域可以是片上闪存、RAM等。

外设地址空间是用于访问外设寄存器的存储器区域。在stm32F10x中,每个外设都有一组寄存器用于配置和控制该外设。这些寄存器被映射到特定的地址空间中,通过读写这些地址来配置和控制外设。

stm32F10x的存储器地址映射可以通过文档或头文件来了解具体的映射关系。通常,片上闪存会映射到内核地址空间的代码存储器区域,RAM会映射到内核地址空间的数据存储器区域,而外设寄存器则会映射到外设地址空间的特定地址范围。

理解存储器地址映射对于开发stm32F10x的应用程序非常重要。通过了解存储器地址映射,开发者可以正确地访问和操作存储器和外设,实现所需的功能。

3、Ubuntu(x86)系统编程

在这里插入图片描述

下图为输出。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基于C++&OPENCV 的全景图像拼接 C++是一种广泛使用的编程语言,它是由Bjarne Stroustrup于1979年在新泽西州美利山贝尔实验室开始设计开发的。C++是C语言的扩展,旨在提供更强大的编程能力,包括面向对象编程和泛型编程的支持。C++支持数据封装、继承和多态等面向对象编程的特性和泛型编程的模板,以及丰富的标准库,提供了大量的数据结构和算法,极大地提高了开发效率。12 C++是一种静态类型的、编译式的、通用的、大小写敏感的编程语言,它综合了高级语言和低级语言的特点。C++的语法与C语言非常相似,但增加了许多面向对象编程的特性,如类、对象、封装、继承和多态等。这使得C++既保持了C语言的低级特性,如直接访问硬件的能力,又提供了高级语言的特性,如数据封装和代码重用。13 C++的应用领域非常广泛,包括但不限于教育、系统开发、游戏开发、嵌入式系统、工业和商业应用、科研和高性能计算等领域。在教育领域,C++因其结构化和面向对象的特性,常被选为计算机科学和工程专业的入门编程语言。在系统开发领域,C++因其高效性和灵活性,经常被作为开发语言。游戏开发领域中,C++由于其高效性和广泛应用,在开发高性能游戏和游戏引擎中扮演着重要角色。在嵌入式系统领域,C++的高效和灵活性使其为理想选择。此外,C++还广泛应用于桌面应用、Web浏览器、操作系统、编译器、媒体应用程序、数据库引擎、医疗工程和机器人等领域。16 学习C++的关键是理解其核心概念和编程风格,而不是过于深入技术细节。C++支持多种编程风格,每种风格都能有效地保证运行时间效率和空间效率。因此,无论是初学者还是经验丰富的程序员,都可以通过C++来设计和实现新系统或维护旧系统。3

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值