GCC编译器-<嵌入式Linux应用程序开发标准教程>-华清远见

2023-11-11


3.3 gcc编译器
GNU CC(简称为gcc)是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Object C等
语言编写的程序。gcc不仅功能强大,而且可以编译如C、C++、Object C、Java、Fortran、Pascal、Modula-3
和Ada等多种语言,而且gcc是一个交叉平台编译器,它能够在当前CPU平台上为多种不同体系结构的
硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译。本章中的示例,除非特别注明,否则均采用
4.x.x的gcc版本。
表3.6所示为gcc支持编译源文件的后缀及其解释。
表3.6 gcc所支持后缀名解释
后  缀  名  所对应的语言  后  缀  名  所对应的语言
.c  C原始程序  .s/.S  汇编语言原始程序
.C/.cc/.cxx  C++原始程序  .h  预处理文件(头文件)
.m  Objective-C原始程序  .o  目标文件
.i  已经过预处理的C原始程序  .a/.so  编译后的库文件
.ii  已经过预处理的C++原始程序  … … 
3.3.1 gcc编译流程解析
如本章开头提到的,gcc的编译流程分为了4个步骤,分别为:
  预处理(Pre-Processing);
  编译(Compiling);
  汇编(Assembling);
  链接(Linking)。
下面就具体来查看一下gcc是如何完成以上4个步骤的。
首先看一下hello.c的源代码:
#include <stdio.h> 

int main() 

printf("Hello! This is our embedded world!\n"); 
return 0; 

(1)预处理阶段。
在该阶段,对包含的头文件(#include)和宏定义(#define、#ifdef等)进行处理。在上述代码的预处理过
程中,编译器将包含的头文件stdio.h编译进来,并且用户可以使用gcc的选项“-E”进行查看,该选项的
作用是让gcc在预处理结束后停止编译过程。
注意
gcc指令的一般格式为:gcc [选项] 要编译的文件[选项] [目标文件] 
其中,目标文件可缺省,gcc默认生成可执行的文件,名为:编译文件.out 
[root@localhost gcc]# gcc –E hello.c –o hello.i
在此处,选项“-o”是指目标文件,由表3.6可知,“.i”文件为已经过预处理的C程序。以下列出了hello.i
文件的部分内容:
typedef int (*__gconv_trans_fct) (struct __gconv_step *, 
struct __gconv_step_data *, void *, 
__const unsigned char *, 
__const unsigned char **, 
__const unsigned char *, unsigned char **, 
size_t *); 

# 2 "hello.c" 2 
int main() 

printf("Hello! This is our embedded world!\n"); 
return 0; 

由此可见,gcc确实进行了预处理,它把“stdio.h”的内容插入hello.i文件中。
(2)编译阶段。
接下来进行的是编译阶段,在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代
码实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。用户可以使用“-S”选项来进行查看,
该选项只进行编译而不进行汇编,结果生成汇编代码。
[root@localhost gcc]# gcc –S hello.i –o hello.s
以下列出了hello.s的内容,可见gcc已经将其转化为汇编代码了,感兴趣的读者可以分析一下这一个简单
的C语言小程序是如何用汇编代码实现的。
.file "hello.c" 
.section .rodata 
.align 4 
.LC0: 
.string "Hello! This is our embedded world!" 
.text 
.globl main 
.type main, @function 
main: 
pushl %ebp 
movl %esp, %ebp 

subl $8, %esp 
andl $-16, %esp 
movl $0, %eax 
addl $15, %eax 
addl $15, %eax 
shrl $4, %eax 
sall $4, %eax 
subl %eax, %esp 
subl $12, %esp 
pushl $.LC0 
call puts 
addl $16, %esp 
movl $0, %eax 
leave 
ret 
.size main, .-main 
.ident "GCC: (GNU) 4.0.0 200XYZ19 (Red Hat 4.0.0-8)" 
.section .note.GNU-stack,"",@progbits 
(3)汇编阶段。
汇编阶段是把编译阶段生成的“.s”文件转成目标文件,读者在此使用选项“-c”就可看到汇编代码已转
化为“.o”的二进制目标代码了,如下所示:
[root@localhost gcc]# gcc –c hello.s –o hello.o
(4)链接阶段。
在成功编译之后,就进入了链接阶段。这里涉及一个重要的概念:函数库。
读者可以重新查看这个小程序,在这个程序中并没有定义“printf”的函数实现,且在预编译中包含进
的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现“printf”函数的
呢?最后的答案是:系统把这些函数的实现都放到名为libc.so.6的库文件中去了,在没有特别指定时,
gcc会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到libc.so.6函数库中去,这样就能调
用函数“printf”了,而这也正是链接的作用。
函数库有静态库和动态库两种。静态库是指编译链接时,将库文件的代码全部加入可执行文件中,因此生
成的文件比较大,但在运行时也就不再需要库文件了。其后缀名通常为“.a”。动态库与之相反,在编译链
接时并没有将库文件的代码加入可执行文件中,而是在程序执行时加载库,这样可以节省系统的开销。一
般动态库的后缀名为“.so”,如前面所述的libc.so.6就是动态库。gcc在编译时默认使用动态库。
完成了链接之后,gcc就可以生成可执行文件,如下所示。
[root@localhost gcc]# gcc hello.o –o hello
运行该可执行文件,出现的正确结果如下。
[root@localhost gcc]# ./hello 
Hello! This is our embedded world! 
3.3.2 gcc编译选项分析
gcc有超过100个可用选项,主要包括总体选项、告警和出错选项、优化选项和体系结构相关选项。以下
对每一类中最常用的选项进行讲解。

(1)常用选项。
gcc的常用选项如表3.7所示,很多在前面的示例中已经有所涉及。
表3.7 gcc常用选项列表
选  项  含  义
-c  只编译不链接,生成目标文件“.o”
-S  只编译不汇编,生成汇编代码
-E  只进行预编译,不做其他处理
-g  在可执行程序中包含标准调试信息
-o file  将file文件指定为输出文件
-v  打印出编译器内部编译各过程的命令行信息和编译器的版本
-I dir  在头文件的搜索路径列表中添加dir目录
前一小节已经讲解了“-c”、“-E”、“-o”、“-S”选项的使用方法,在此主要讲解另外2个非常常用的库依
赖选项“-I dir”。
  “-I dir”
正如上表中所述,“-I dir”选项可以在头文件的搜索路径列表中添加dir目录。由于Linux中头文件都默认
放到了“/usr/include/”目录下,因此,当用户希望添加放置在其他位置的头文件时,就可以通过“-I dir”
选项来指定,这样,gcc就会到相应的位置查找对应的目录。
比如在“/root/workplace/gcc”下有两个文件:
/* hello1.c */ 
#include<my.h> 
int main() 

printf("Hello!!\n"); 
return 0; 

/* my.h */ 
#include<stdio.h> 
这样,就可在gcc命令行中加入“-I”选项:
[root@localhost gcc] gcc hello1.c –I /root/workplace/gcc/ -o hello1
这样,gcc就能够执行出正确结果。
小知识
在include 语句中,“<>”表示在标准路径中搜索头文件,““””表示在本目
录中搜索。故在上例中,可把hello1.c的“#include<my.h>”改为“#include “my.h””,
就不需要加上“-I”选项了。
(2)库选项。
gcc库选项如表3.8所示。
表3.8 gcc库选项列表
选  项  含  义
-static  进行静态编译,即链接静态库,禁止使用动态库
-shared 
1.可以生成动态库文件
2.进行动态编译,尽可能地链接动态库,只有当没有动态库时才会链接同名的静态库
(默认选项,即可省略)
-L dir  在库文件的搜索路径列表中添加dir目录 

-lname 
链接称为libname.a(静态库)或者libname.so(动态库)的库文件。若两个库都存在,
则根据编译方式(-static还是-shared)而进行链接
-fPIC(或-fpic)
生成使用相对地址的位置无关的目标代码(Position Independent Code)。然后通常使用
gcc的-static选项从该PIC目标文件生成动态库文件
我们通常需要将一些常用的公共函数编译并集成到二进制文件(Linux的ELF格式文件),以便其他程
复地使用该文件中的函数,此时将这种文件叫做函数库,使用函数库不仅能够节省很多内存和存储器的
源,而且更重要的是大大降低开发难度和开销,提高开发效率并增强程序的结构性。实际上,在Linux
个程序都会链接到一个或者多个库。比如使用C函数的程序会链接到C运行时库,Qt应用程序会链接
支持的相关图形库等。
函数库有静态库和动态库两种,静态库是一系列的目标文件(.o 文件)的归档文件(文件名格
libname.a),如果在编译某个程序时链接静态库,则链接器将会搜索静态库,从中提取出它所需要的
文件并直接复制到该程序的可执行二进制文件(ELF格式文件)之中;动态库(文件名格式为libname
版本号.次版本号.发行号])在程序编译时并不会被链接到目标代码中,而是在程序运行时才被载入。
下面举一个简单的例子,讲解如何怎么创建和使用这两种函数库。
首先创建unsgn_pow.c文件,它包含unsgn_pow()函数的定义,具体代码如下所示。
/* unsgn_pow.c:库程序*/ 
unsigned long long unsgn_pow(unsigned int x, unsigned int y) 

unsigned long long res = 1; 
if (y == 0) 

res = 1; 

else if (y == 1) 

res = x; 

else 

res = x * unsgn_pow(x, y - 1); 

return res; 

然后创建pow_test.c文件,它会调用unsgn_pow()函数。
/* pow_test.c */ 
#include <stdio.h> 
#include <stdlib.h> 
int main(int argc, char *argv[]) 

 unsigned int x, y; 
 unsigned long long res; 
 if ((argc < 3) || (sscanf(argv[1], "%u", &x) != 1) 
|| (sscanf(argv[2], "%u", &y)) != 1) 

  printf("Usage: pow base exponent\n"); 
exit(1); 

 res = unsgn_pow(x, y); 
 printf("%u ^ %u = %u\n", x, y, res); 
exit(0); 

我们用unsgn_pow.c文件可以制作一个函数库。下面分别讲解怎么生成静态库和动态库。
  静态库的创建和使用。
创建静态库比较简单,使用归档工具ar将一些目标文件集成在一起。
[root@localhost lib]# gcc -c unsgn_pow.c
[root@localhost lib]# ar rcsv libpow.a unsgn_pow.o 
a - unsgn_pow.o 
下面编译主程序,它将会链接到刚生成的静态库libpow.a。具体运行结果如下所示。
[root@localhost lib]# gcc -o pow_test pow_test.c -L. –lpow 
[root@localhost lib]# ./pow_test 2 10
2 ^ 10 = 1024 
其中,选项“-L dir”的功能与“-I dir”类似,能够在库文件的搜索路径列表中添加dir目录,而“-lna
选项指示编译时链接到库文件libname.a 或者libname.so。本实例中,程序pow_test.c需要使用当前目录

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GCC编译器-<嵌入式Linux应用程序开发标准教程>-华清远见 的相关文章

  • X11 模式对话框

    如何使用 Xlib 在 X11 中创建模式对话框 模态对话框是一个位于应用程序其他窗口之上的窗口 就像瞬态窗口一样 并且拒绝将焦点给予应用程序的其他窗口 在 Windows 中 当试图从模态窗口夺取焦点时 模态也会通过闪 烁模态窗口的标题栏
  • 无法在 64 位 Linux 上从汇编 (yasm) 代码调用 C 标准库函数

    我有一个函数foo以汇编语言编写 并在 Linux Ubuntu 64 位上使用 yasm 和 GCC 编译 它只是使用以下命令将消息打印到标准输出puts 如下所示 bits 64 extern puts global foo secti
  • linux下写入后崩溃

    如果我使用 write 将一些数据写入磁盘上的文件会发生什么 但我的应用程序在刷新之前崩溃了 如果没有系统故障 是否可以保证我的数据最终会刷新到磁盘 如果您正在使用write 并不是fwrite or std ostream write 那
  • 如何访问 mmaped /dev/mem 而不导致 Linux 内核崩溃?

    我有一个简单的程序 尝试访问用户空间中的物理内存 其中内核存储第一个结构页 在 64 位机器上 该地址是 内核虚拟地址 ffffea0000000000 物理地址 0000620000000000 我正在尝试通过用户空间中的 mmap 访问
  • 从c调用汇编函数

    我试图从 c 调用汇编函数 但我不断收到错误 text globl integrate type integrate function integrate push ebp mov esp ebp mov 0 edi start loop
  • 推荐用于小型站点的 IRC 服务器 (ircd)? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 情况 我想使用 IRC 机器人作为我正在研究的其他代码的通用通信接口 服务器硬件陈旧且内存不足 但运行在相对最新的 Debian GNU
  • 码头无故停止

    我需要经验丰富的码头用户的建议 我在负载均衡器 亚马逊云 后面维护着 2 台 Linux 机器 使用 Jetty 9 0 3 有时我的 Jetty 容器会被 Thread 2 无故关闭 同时地 显示以下日志并且容器无故停止 没有错误 没有例
  • 从 php/linux 获取 pdf 的布局模式(横向或纵向)

    给定一个 PDF 如何使用 PHP lib 或 Linux 命令行工具获取 PDF 的布局模式 或相对宽度 高度 Using http www tecnick com public code cp dpage php aiocp dp tc
  • 在 Linux 上创建线程与进程的开销

    我试图回答在 python 中创建线程与进程有多少开销的问题 我修改了类似问题的代码 该问题基本上运行一个带有两个线程的函数 然后运行带有两个进程的相同函数并报告时间 import time sys NUM RANGE 100000000
  • 无需 root 访问权限即可安装 zsh? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 有可能 以及如何 我确实需要在几台具有 ssh 访问权限 但没有 root 访问权限 的远程计算机上使用此功能 下载 zsh wget O zsh t
  • 如何重命名 .tar.gz 文件而不提取内容并在 UBUNTU 中创建新的 .tar.gz 文件?

    我有一个命令将创建一个新的 tar gz现有文件中的文件 sudo tar zcvf Existing tar gz New tar gz 该命令将创建一个新的New tar gz从现有的文件Existing tar gz file 谁能告
  • 运行此处编译的 C 程序会导致在另一台服务器上找不到 GLIBC 库错误 - 是我的错还是他们的错?

    此处编译的 C 程序在我们的 Ubuntu 服务器上运行良好 但是当其他人尝试在他们的特定 Linux 服务器上运行它时 他们会收到以下错误 myprog install lib tls libc so 6 version GLIBC 2
  • 将node.js +expressjs应用程序的NODE_ENV设置为ubuntu下的守护进程

    我按照这些说明让守护进程正常工作 http kevin vanzonneveld net techblog article run nodejs as a service on ubuntu karmic http kevin vanzon
  • 如何在 Linux/OS X 上温和地终止 Firefox 进程

    我正在使用 Firefox 进行一些自动化操作 尽管我可以从 shell 打开 Firefox 窗口 但我无法正确终止它 如果我kill火狐进程与kill 3 or kill 2当我下次打开新的 Firefox 窗口时 命令会询问我是否要在
  • 完整的 C++ i18n gettext()“hello world”示例

    我正在寻找完整的 i18ngettext 你好世界的例子 我已经开始了一个基于的脚本使用 GNU gettext 的本机语言支持教程 https web archive org web 20130330233819 http oriya s
  • 在非实时操作系统/内核上执行接近实时任务的最佳方法是什么?

    在一台 GNU Linux 机器上 如果想要执行 实时 亚毫秒级时间关键 任务 您几乎总是必须经历漫长 复杂且容易出现问题的内核补丁过程 以提供足够的支持 1 http en wikipedia org wiki RTLinux Backg
  • Visual Studio - X11:缺少 DISPLAY 环境变量

    我正在使用 Visual Studio 2019 Enterprise 开发跨平台 Windows Linux x64 GUI 应用程序 在这个 2019 版本中 我们可以使用 Visual Studio调试平台 Windows 本机 和
  • 如何回忆上一个 bash 命令的参数?

    Bash 有没有办法回忆上一个命令的参数 我通常这样做vi file c其次是gcc file c Bash 有没有办法回忆上一个命令的参数 您可以使用 or 调用上一个命令的最后一个参数 Also Alt can be used to r
  • 如果输入被重定向则执行操作

    我想知道如果我的输入被重定向 我应该如何在 C 程序中执行操作 例如 假设我有已编译的程序 prog 并且我将输入 input txt 重定向到它 我这样做 prog lt input txt 我如何在代码中检测到这一点 一般来说 您无法判
  • 如何在 bash_profile 文件中添加导出语句?

    我正在尝试了解是否必须添加导出语句来在 bash profile 文件中设置变量 我该怎么做呢 例如 如果我必须添加 export AX name 那么我应该将其简单地写在文件末尾还是我还需要编写其他内容 简单写一下export AS na

随机推荐

  • FileReader简介

    前言 FileReader是一种异步文件读取机制 结合input file可以很方便的读取本地文件 input file 在介绍FileReader之前 先简单介绍input的file类型
  • Chrome插件链接

    常用Chrome插件整理 其它好用的插件 欢迎大家留言分享 输入ID下载点击下载 1 已安装插件里可以查看ID 2 安装链接里面也有 https chrome google com webstore detail idm integrati
  • vivado烧录程序搜索不到设备,烧录卡住不动

    烧录程序找不到设备或者设备不可用 烧录过程卡主不动 可以排除驱动问题的前提 解决方法 1 先观察jtag的指示灯 红灯有问题 可能fpga电源未开 接口松动 观察板子的初始程序是否启动成功 如果没有启动成功 jtag指示器也会是绿的 但是搜
  • springboot 过滤器异常处理,filter exception catch

    参考地址 java How to manage exceptions thrown in filters in Spring Stack Overflow
  • 经验积累①:关于设备程序的版本迭代方案详解

    关于设备程序的版本迭代方案详解 一 案例描述 对于嵌入式应用层来说 需要对设备的很多参数进行保存 为了使得这些配置参数掉电不丢失 因此在flash中生成配置文件用于保存设备参数 每当设备重启后 将参数读出 重发给设备 由于生成了可变的配置文
  • event_base_loop

    函数 int event base loop struct event base int 等待事件被触发 然后调用它们的回调函数 这是 event base dispatch的更灵活版本 默认情况下 这个循环会一直运行 直到没有添加的事件
  • 在macOS下安装和配置MySQL数据库

    一 到社区下载安装包 官网地址 MySQL MySQL Community Downloadshttps dev mysql com downloads MySQL Download MySQL Community Serverhttps
  • 身边的逻辑学——过度概括(1)

    过度概括 overgeneralization 概括与过度概括有何不同 概括知识是暂定的 因为它基于对经验的概括 原则上 下一个经验很可能出乎你的意料之外 使你 我们每个人 不得不对先前概括得出的结论感到怀疑 了解概括的本质有助于发现真理
  • C/C++ vs2017连接MySQL数据库 - 增删改查(详细步骤)

    在开发中 数据库是必不可少 这篇文章将介绍使用C C 如何进行连接MySQL数据库 并实现增删改查操作 注意 此篇文章所讲的是C C 如何操控MySQL进行简单的 常用的 增删改查 的操作 目录 一 配置Visual Studio 二 C
  • 不可不说的Java“锁”事

    前言 Java提供了种类丰富的锁 每种锁因其特性的不同 在适当的场景下能够展现出非常高的效率 本文旨在对锁相关源码 本文中的源码来自JDK 8 使用场景进行举例 为读者介绍主流锁的知识点 以及不同的锁的适用场景 Java中往往是按照是否含有
  • ACL访问控制列表原理及实例

    目录 一 ACL概述 1 ACL访问控制列表作用 2 ACL访问控制列表工作原理 3 ACL访问控制列表处理过程原则 二 ACL访问控制列表类型 1 标准访问控制列表 2 扩展访问控制列表 三 配置实例 1 要求 用标准访问控制列表 vla
  • qt modules public internal 私有头文件 private

    qt对源码进行了分层 把数据成员都单独拉到一个 p h的头文件中 形成internal部分的接口类Qt之二进制兼容 qt p h 文件的用途是什么 qt不建议用户使用internal部分的接口类 因为不同平台和不用版本的qt interna
  • sort函数的时间、空间复杂度

    sort函数进行排序的时间复杂度为n log2n 原理 不是简单的快排 STL的sort 算法 数据量大时采用Quick Sort 分段递归排序 一旦分段后的数据量小于某个门槛 为避免Quick Sort的递归调用带来过大的额外负荷 就改用
  • Grade for Android(从 Gradle 和 AS 开始)

    http www open open com lib view open1451536597026 html 正如大家所见 这是本英文书 而由于国内的gradle翻译资料不全 所以特次开辟专栏 翻译gradle for android这本书
  • 基于JWT的springboot权限验证技术实现

    JWT简介 Json Web Token JWT JSON网络令牌 是为了在网络应用环境间传递声明而制定的一种基于JSON的开放标准 RFC 7519 JWT是一个轻便的安全跨平台传输格式 定义了一个紧凑的自包含的方式用于通信双方之间以 J
  • 通过Navicat创建ER图技巧笔记

    1 通过已有数据库逆向创建模型时 切忌一次生成所有表ER图 建议先将所有表添加到模型 然后依次从上至下添加到图表 2 单张图表可以通过图表模式进行重命名 在左侧图表窗口最上方 右键当前图表 gt 重命名 gt 修改 3 单张图表不宜过大 4
  • 解决vscode jupyter启动kernel错误的方案Failed to connect to Jupyter notebook.

    最近在vscode里面运行 ipynb文件的时候经常性地会出现报错情况 报错的图片内容如下 Failed to connect to Jupyter notebook 经过网上一番查阅方法之后 终于将配件搭建好了 解决方法为输入如下命令 p
  • 【Unity 学习笔记】动画系统(续)和事件

    在上一篇笔记中 我们使用Unity的动画系统实现了移动和跳跃的动画 在这一篇笔记中我们将使用Unity事件的方式来实现受伤和死亡的动画 首先创建一个受伤动画 由于素材中没有现成的受伤动画 我们只能手动创建一个简陋的闪烁动画 主要现实在角色受
  • java变量的类别和作用范围

    1 类别及作用范围 类变量 在类中用static修饰的字段 存储位置 方法区 生命周期 随着JVM的消亡而消失 生命周期最长 局部变量 在方法中存在的 或者形参 存储位置 栈帧中 生命周期 随着方法的调用而存在 生命周期最短 实例变量 在类
  • GCC编译器-<嵌入式Linux应用程序开发标准教程>-华清远见

    3 3 gcc编译器 GNU CC 简称为gcc 是GNU项目中符合ANSI C标准的编译系统 能够编译用C C 和Object C等 语言编写的程序 gcc不仅功能强大 而且可以编译如C C Object C Java Fortran P