《嵌入式Linux应用程序开发详解-第3 章 Linux 下的C 编程.pdf》由会员分享,可在线阅读,更多相关《嵌入式Linux应用程序开发详解-第3 章 Linux 下的C 编程.pdf(49页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、华清远见嵌入式培训专家 http:/ 华清远见培训教材 “黑色经典”系列之嵌入式“黑色经典”系列之嵌入式 Linux 应用程序开发详解应用程序开发详解 第 3 章 Linux 下的 C 编程基础 本章目标 在熟悉了 Linux 常见命令,能够在 Linux 中熟练操作之后,本章将带领读者学习在 Linux中进行 C 语言编程的基本技能。学习了本章后,读者能够掌握如下内容。熟悉 Linux 系统下的开发环境 熟悉 Vi 的基本操作 熟练 Emacs 的基本操作 熟悉 Gcc 编译器的基本原理 熟练使用 Gcc 编译器的常用选项 熟练使用 Gdb 调试技术 熟悉 Makefile 基本原理及语法规
2、范 熟练使用 autoconf 和 automake 来生成 Makefile QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 3.1 Linux 下 C 语言编程概述 3.1.1 C 语言简单回顾 C 语言最早是由贝尔实验室的 Dennis Ritchie 为了 UNIX 的辅助开发而编写的,它是在 B语言的基础上开发出来的。尽管语言不是专门针对 UNIX 操作系统或机器编写的,但它与UNIX 系统的关系十分紧密。由于它的硬件无关性和可移植性,使 C 语言逐渐成为世界上使用最广泛计算机语言。为了进一步规范 C 语言的硬件无关性,1987 年,美国国
3、家标准协会(ANSI)根据 C 语言问世以来各种版本对 C 语言的发展和扩充,制定了新的标准,称为 ANSI C。ANSI C 语言比原来的标准 C 语言有了很大的发展。目前流行的 C 语言编译系统都是以它为基础的。C 语言的成功并不是偶然的,它强大的功能和它的可移植性让它能在各种硬件平台上游刃自如。总体而言,C 语言有如下特点。C 语言是“中级语言”。它把高级语言的基本结构和语句与低级语言的实用性结合起来。C 语言可以像汇编语言一样对位、字节和地址进行操作,而这三者是计算机最基本的工作单元。C 语言是结构化的语言。C 语言采用代码及数据分隔,使程序的各个部分除了必要的信息交流外彼此独立。这种
4、结构化方式可使程序层次清晰,便于使用、维护以及调试。C 语言是以函数形式提供给用户的,这些函数可方便地调用,并具有多种循环、条件语句控制程序流向,从而使程序完全结构化。C 语言功能齐全。C 语言具有各种各样的数据类型,并引入了指针概念,可使程序效率更高。另外,C 语言也具有强大的图形功能,支持多种显示器和驱动器,而且计算功能、逻辑判断功能也比较强大,可以实现决策目的。C 语言可移植性强。C 语言适合多种操作系统,如 DOS、Windows、Linux,也适合多种体系结构,因此尤其适合在嵌入式领域的开发。3.1.2 Linux 下 C 语言编程环境概述 Linux 下的 C 语言程序设计与在其他
5、环境中的 C 程序设计一样,主要涉及到编辑器、编译链接器、调试器及项目管理工具。现在我们先对这 4 种工具进行简单介绍,后面会对其一一进行讲解。(1)编辑器 Linux 下的编辑器就如 Windows 下的 word、记事本等一样,完成对所录入文字的编辑功能。Linux 中最常用的编辑器有 Vi(Vim)和 Emacs,QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解 第 3 章、Linux 下的 C 编程基础 华清远见培训教材 图 3.1 编译过程 它们功能强大,使用方便,广受编程爱好者的喜爱。在本书中,着重介绍 Vi 和 Emacs。(2)编译链接器 编译是指源代
6、码转化生成可执行代码的过程,它所完成工作主要如图 3.1 所示。可见,在编译过程是非常复杂的,它包括词法、语法和语义的分析、中间代码的生成和优化、符号表的管理和出错处理等。在 Linux 中,最常用的编译器是 Gcc 编译器。它是 GNU推出的功能强大、性能优越的多平台编译器,其执行效率与一般的编译器相比平均效率要高20%30%,堪称为 GNU 的代表作品之一。(3)调试器 调试器并不是代码执行的必备工具,而是专为程序员方便调试程序而用的。有编程经验的读者都知道,在编程的过程当中,往往调试所消耗的时间远远大于编写代码的时间。因此,QQ:313638714http:/华清远见嵌入式培训专家 ht
7、tp:/ 华清远见培训教材 有一个功能强大、使用方便的调试器是必不可少的。Gdb 是绝大多数 Linux 开发人员所使用的调试器,它可以方便地设置断点、单步跟踪等,足以满足开发人员的需要。(4)项目管理器 Linux 中的项目管理器“make”有些类似于 Windows 中 Visual C+里的“工程”,它是一种控制编译或者重复编译软件的工具,另外,它还能自动管理软件编译的内容、方式和时机,使程序员能够把精力集中在代码的编写上而不是在源代码的组织上。3.2 进入 Vi Linux 系统提供了一个完整的编辑器家族系列,如 Ed、Ex、Vi 和 Emacs 等。按功能它们可以分为两大类:行编辑器
8、(Ed、Ex)和全屏幕编辑器(Vi、Emacs)。行编辑器每次只能对一行进行操作,使用起来很不方便。而全屏幕编辑器可以对整个屏幕进行编辑,用户编辑的文件直接显示在屏幕上,从而克服了行编辑的那种不直观的操作方式,便于用户学习和使用,具有强大的功能。Vi 是 Linux 系统的第一个全屏幕交互式编辑程序,它从诞生至今一直得到广大用户的青睐,历经数十年仍然是人们主要使用的文本编辑工具,足以见其生命力之强,而强大的生命力是其强大的功能带来的。由于大多数读者在此之前都已经用惯了 Windows 的 word等编辑器,因此,在刚刚接触时总会或多或少不适应,但只要习惯之后,就能感受到它的方便与快捷。3.2.
9、1 Vi 的模式 Vi 有 3 种模式,分别为命令行模式、插入模式及命令行模式各模式的功能,下面具体进行介绍。(1)命令行模式 用户在用 Vi 编辑文件时,最初进入的为一般模式。在该模式中可以通过上下移动光标进行“删除字符”或“整行删除”等操作,也可以进行“复制”、“粘贴”等操作,但无法编辑文字。(2)插入模式 只有在该模式下,用户才能进行文字编辑输入,用户课按ESC键回到命令行模式。(3)底行模式 在该模式下,光标位于屏幕的底行。用户可以进行文件保存或退出操作,也可以设置编辑环境,如寻找字符串、列出行号等。3.2.2 Vi 的基本流程(1)进入 Vi,即在命令行下键入 Vi hello(文件
10、名)。此时进入的是命令行模式,光标位于屏幕的上方,如图 3.2 所示。(2)在命令行模式下键入 i 进入到插入模式,如图 3.3 所示。可以看出,在屏幕底部显示有“插入”表示插入模式,在该模式下可以输入文字信息。QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解 第 3 章、Linux 下的 C 编程基础 华清远见培训教材 图 3.2 进入 Vi 命令行模式 图 3.3 进入 Vi 插入模式 QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材(3)最后,在插入模式中,输入“Esc”,则当前模式转入命令行模式,并在底行行中输入“:w
11、q”(存盘退出)进入底行模式,如图 3.4 所示。这样,就完成了一个简单的 Vi 操作流程:命令行模式插入模式底行模式。由于 Vi在不同的模式下有不同的操作功能,因此,读者一定要时刻注意屏幕最下方的提示,分清所在的模式。图 3.4 进入 Vi 底行模式 3.2.3 Vi 的各模式功能键(1)命令行模式常见功能键如表 3.1 所示。表 3.1 Vi 命令行模式功能键 目 录 目 录 内 容 I 切换到插入模式,此时光标当于开始输入文件处 A 切换到插入模式,并从目前光标所在位置的下一个位置开始输入文字 O 切换到插入模式,且从行首开始插入新的一行 ctrl+b 屏幕往“后”翻动一页 ctrl+f
12、 屏幕往“前”翻动一页 ctrl+u 屏幕往“后”翻动半页 ctrl+d 屏幕往“前”翻动半页 QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解 第 3 章、Linux 下的 C 编程基础 华清远见培训教材 0(数字 0)光标移到本行的开头 G 光标移动到文章的最后 nG 光标移动到第 n 行$移动到光标所在行的“行尾”n 光标向下移动 n 行/name 在光标之后查找一个名为 name 的字符串?name 在光标之前查找一个名为 name 的字符串 X 删除光标所在位置的“后面”一个字符 续表 目 录 目 录 内 容 X 删除光标所在位置的“前面”一个字符 dd 删
13、除光标所在行 ndd 从光标所在行开始向下删除 n 行 yy 复制光标所在行 nyy 复制光标所在行开始的向下 n 行 p 将缓冲区内的字符粘贴到光标所在位置(与 yy搭配)U 恢复前一个动作 (2)插入模式的功能键只有一个,也就是 Esc 退出到命令行模式。(3)底行模式常见功能键如表 3.2 所示。表 3.2 Vi 底行模式功能键 目 录 目 录 内 容:w 将编辑的文件保存到磁盘中:q 退出 Vi(系统对做过修改的文件会给出提示):q!强制退出 Vi(对修改过的文件不作保存):wq 存盘后退出:w filename 另存一个命为 filename 的文件:set nu 显示行号,设定之后
14、,会在每一行的前面显示对应行号:set nonu 取消行号显示 注意 Vi 的升级版 Vim 已经问世了,功能相当强大,且保持与 Vi 的 90%相兼容,因此,感兴趣的读者可以查看相关资料进行学习。QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 3.3 初探 Emacs 正如前面所述,Vi 是一款功能非常强大的编辑器,它能够方便、快捷、高效地完成用户的任务,那么,在此再次向读者介绍另一款编辑器是否多此一举呢?答案是否定的。因为 Emacs 不仅仅是一款功能强大的编译器,而且是一款融合编辑、编译、调试于一体的开发环境。虽然,它没有 Visual Sdi
15、ao 一样绚丽的界面,但是它可以在没有图形显示的终端环境下出色的工作,相信追求强大功能和工作效率的任务并不会介意它朴素的界面的。Emacs 的使用和 Vi 截然不同。在 Emacs 里,没有类似于 Vi 的 3 种“模式”。Emacs只有一种模式,也就是编辑模式,而且它的命令全靠功能键完成。因此,功能键也就相当重要了。但 Emacs 却还使用一个不同 Vi 的“模式”,它的“模式”是指各种辅助环境。比如,当编辑普通文本时,使用的是“文本模式(Txt Mode)”,而当他们写程序时,使用的则是如“c模式”、“Shell 模式”等。下面,首先来介绍一下 Emacs 中作为编辑器的使用方法,以帮助读
16、者熟悉 Emacs 的环境。注释 Emacs 缩写注释:C-表示按住 Ctrl 键的同时键入字符。因此,C-f 就表示按住 Ctrl 键同时键入 f。M-表示当键入字符时同时按住 Meta 或 Edit 或 Alt 键(通常为 Alt 键)。3.3.1 Emacs 的基本操作 1Emacs 安装 现在较新版本的 Linux(如本书中所用的 Red Hat Enterprise 4 AS)的安装光盘中一般都自带有 Emacs 的安装包,用户可以通过安装光盘进行安装(一般在第 2 张光盘中)。2启动 Emacs 安装完 Emacs 之后,只需在命令行键入“emacs 文件名”(若缺省文件名,也可在
17、 emacs编辑文件后另存时指定),也可从“编程”“emacs”打开,3.5 图中所示的就是从“编程”“emacs”打开的 Emacs 欢迎界面。QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解 第 3 章、Linux 下的 C 编程基础 华清远见培训教材 图 3.5 Emacs 欢迎界面 接着可单击任意键进入 Emacs 的工作窗口,如图 3.6 所示。从图中可见,Emacs 的工作窗口分为上下两个部分,上部为编辑窗口,底部为命令显示窗口,用户执行功能键的功能都会在底部有相应的显示,有时也需要用户在底部窗口输入相应的命令,如查找字符串等。QQ:313638714ht
18、tp:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 图 3.6 Emacs 的工作窗口 3进入 Emacs 在进入 Emacs 后,即可进行文件的编辑。由于 Emacs 只有一种编辑模式,因此用户无需进行模式间的切换。下面介绍 Emacs 中基本编辑功能键。(1)移动光标 虽然在 Emacs 中可以使用“上”、“下”、“左”、“右”方向键来移动单个字符,但笔者还是建议读者学习其对应功能键,因为它们不仅能在所有类型的终端上工作,而且读者将会发现在熟练使用之后,输入这些 Ctrl 加字符会比按方向键快很多。下表 3.3 列举了 Emacs 中光标移动的常见功能键。表 3.3 Emacs
19、 光标移动功能键 目 录 目 录 内 容 目 录 目 录 内 容 C-f 向前移动一个字符 M-b 向后移动一个单词 C-b 向后移动一个字符 C-a 移动到行首 C-p 移动到上一行 C-e 移动到行尾 C-n 移动到下一行 M-(M 加“小于号”)移动光标到整个文本的开头 QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解 第 3 章、Linux 下的 C 编程基础 华清远见培训教材 M-f 向前移动一个单词 M-(M 加“大于号”)移动光标到整个文本的末尾 (2)剪切和粘贴 在 Emacs 中可以使用“Delete”和“BackSpace”删除光标前后的字符,这和
20、用户之前的习惯一致,在此就不赘述。以词和行为单位的剪切和粘贴功能键如表 3.4 所示。表 3.4 Emacs 剪切和粘贴 目 录 目 录 内 容 目 录 目 录 内 容 M-Delete 剪切光标前面的单词 M-k 剪切从光标位置到句尾的内容 M-d 剪切光标前面的单词 C-y 将缓冲区中的内容粘贴到光标所在的位置 C-k 剪切从光标位置到行尾的内容 C-x u 撤销操作(先操作 C-x,接着再单击 u)注意 在 Emacs 中对单个字符的操作是“删除”,而对词和句的操作是“剪切”,即保存在缓冲区中,以备后面的“粘贴”所用。(3)复制文本 在 Emacs 中的复制文本包括两步:选择复制区域和粘
21、贴文本。选择复制区域的方法是:首先在复制起始点(A)按下“C-Spase”或“C-(C-Shift-2)”使它成为一个表示点,再将光标移至复制结束电(B),再按下“M-w”,就可将 A 与 B 之间的文本复制到系统的缓冲区中。在使用功能键 C-y 将其粘贴到指定位置。(4)查找文本 查找文本的功能键如表 3.5 所示。表 3.5 Emacs 查找文本功能键 目 录 目 录 内 容 C-s 查找光标以后的内容,并在对话框的“I-search:”后输入查找字符串 C-r 查找光标以前的内容,并在对话框的“I-search backward:”后输入查找字符串 (5)保存文档 在 Emacs 中保存
22、文档的功能键为“C-x C-s”(即先操作 C-x,接着再操作 C-s),这时,屏幕底下的对话框会出现如“Wrote/root/workplace/editor/why”字样,如图 3.7 所示。QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 图 3.7 Emacs 中保存文档 另外,Emacs 在编辑时会为每个文件提供“自动保存(auto save)”的机制,而且自动保存的文件的文件名前后都有一个“#”,例如,编辑名为“hello.c”的文件,其自动保存的文件的文件名就叫“#hello.c#”。当用户正常的保存了文件后,Emacs 就会删除这个自动
23、保存的文件。这个机制当系统发生异常时非常有用。(6)退出文档 在 Emacs 中退出文档的功能键为“C-x C-c”。3.3.2 Emacs 的编译概述 正如本节前面所提到的,Emacs 不仅仅是个强大的编译器,它还是一个集编译、调试等于一体的工作环境。在这里,读者将会了解到 Emacs 作为编译器的最基本的概念,感兴趣的读者可以参考Learning GNU Emacs,Second Edition一书进一步深入学习 Emacs。1Emacs 中的模式 正如本节前面提到的,在 Emacs 中并没有像 Vi 中那样的“命令行”、“编辑”模式,只有一种编辑模式。这里所说的“模式”,是指 Emacs
24、 里的各种辅助环境。下面就着重了解一下 C 模式。当我们启动某一文件时,Emacs 会判断文件的类型,从而自动选择相应的模式。当然,用户也可以手动启动各种模式,用功能键“M-x”,然后再输入模式的名称,如图所示 3.8 所示就启动了“C 模式”。QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解 第 3 章、Linux 下的 C 编程基础 华清远见培训教材 图 3.8 Emacs 中选择模式 在强大的 C 模式下,用户拥有“自动缩进”、“注释”、“预处理扩展”、“自动状态”等强大功能。在“C 模式”下编辑代码时,可以用“Tab”键自动的将当前行的代码产生适当的缩进,使代
25、码结构清晰、美观,它也可以指定缩进的规则。源代码要有良好可读性,必须要有良好的注释。在 Emacs 中,用“M-”可以产生一条右缩进的注释。C 模式下是“/*comments*/”形式的注释,C+模式下是“/comments”形式的注释。当用户高亮选定某段文本,然后操作“C-c C-c”,就可以注释该段文字。Emacs 还可以使用 C 预处理其运行代码的一部分,以便让程序员检测宏、条件编译以及include 语句的效果。2Emacs 编译调试程序 Emacs 可以让程序员在 Emacs 环境里编译自己的软件。此时,编辑器把编译器的输出和程序代码连接起来。程序员可以像在Windows 的其他开发
26、工具一样,将出错位置和代码定位联系起来。Emacs 默认的编辑命令是对一个 make(在本章 3.6 节中会详细介绍)的调用。用户可以打开“tool”下的“Compile”进行查看。Emacs 可以支持大量的工程项目,以方便程序员的开发。另外,Emacs 为 Gdb 调试器提供了一个功能齐全的接口。在 Emacs 中使用 Gdb 的时候,程序员不仅能够获得 Gdb 用其他任何方式运行时所具有的全部标准特性,还可以通过接口增强而获得的其他性能。QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 3.4 Gcc 编译器 GNU CC(简称为 Gcc)是 GN
27、U 项目中符合 ANSI C 标准的编译系统,能够编译用 C、C+和 Object C 等语言编写的程序。Gcc 不仅功能强大,而且可以编译如 C、C+、Object C、Java、Fortran、Pascal、Modula-3 和 Ada 等多种语言,而且 Gcc 又是一个交叉平台编译器,它能够在当前 CPU 平台上为多种不同体系结构的硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译。本章中的示例,除非特别注明,否则均采用 Gcc 版本为 4.0.0。下表 3.6 是 Gcc 支持编译源文件的后缀及其解释。表 3.6 Gcc 所支持后缀名解释 后 缀 名 所对应的语言 后 缀 名 所对应
28、的语言.c C 原始程序.s/.S 汇编语言原始程序.C/.cc/.cxx C+原始程序.h 预处理文件(头文件).m Objective-C 原始程序.o 目标文件.i 已经过预处理的 C 原始程序.a/.so 编译后的库文件.ii 已经过预处理的 C+原始程序 3.4.1 Gcc 编译流程解析 如本章开头提到的,Gcc 的编译流程分为了 4 个步骤,分别为:预处理(Pre-Processing);编译(Compiling);汇编(Assembling);链接(Linking)。下面就具体来查看一下 Gcc 是如何完成 4 个步骤的。首先,有以下 hello.c 源代码:#include i
29、nt main()printf(Hello!This is our embedded world!n);return 0;(1)预处理阶段 在该阶段,编译器将上述代码中的 stdio.h 编译进来,并且用户可以使用 Gcc 的选项“-E”进行查看,该选项的作用是让 Gcc 在预处理结束后停止编译过程。注意 Gcc 指令的一般格式为:Gcc 选项 要编译的文件 选项 目标文件 其中,目标文件可缺省,Gcc 默认生成可执行的文件,命为:编译文件.out QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解 第 3 章、Linux 下的 C 编程基础 华清远见培训教材 root
30、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 ma
31、in()printf(Hello!This is our embedded world!n);return 0;由此可见,Gcc 确实进行了预处理,它把“stdio.h”的内容插入到 hello.i 文件中。(2)编译阶段 接下来进行的是编译阶段,在这个阶段中,Gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc 把代码翻译成汇编语言。用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。rootlocalhost Gcc#Gcc S hello.i o hello.s 以下列出了 hello.s 的内容,可见 Gcc 已
32、经将其转化为汇编了,感兴趣的读者可以分析一下这一行简单的 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 QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 andl$-16,%esp movl$0,%eax addl$15,%eax addl$
33、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 20050519(Red Hat 4.0.0-8).section.note.GNU-stack,progbits (3)汇编阶段 汇编阶段是把编译阶段生成的“.s”文件转成目标文件,读者在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了。如下所示:rootlocal
34、host Gcc#Gcc c hello.s o hello.o (4)链接阶段 在成功编译之后,就进入了链接阶段。在这里涉及到一个重要的概念:函数库。读者可以重新查看这个小程序,在这个程序中并没有定义“printf”的函数实现,且在预编译中包含进的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现“printf”函数的呢?最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,Gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,
35、而这也就是链接的作用。函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。Gcc 在编译时默认使用动态库。完成了链接之后,Gcc 就可以生成可执行文件,如下所示。rootlocalhost Gcc#Gcc hello.o o hello 运行该可执行文件,出现正确的结果如
36、下。QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解 第 3 章、Linux 下的 C 编程基础 华清远见培训教材 rootlocalhost Gcc#./hello Hello!This is our embedded world!3.4.2 Gcc 编译选项分析 Gcc 有超过 100 个的可用选项,主要包括总体选项、告警和出错选项、优化选项和体系结构相关选项。以下对每一类中最常用的选项进行讲解。(1)总体选项 Gcc 的总结选项如表 3.7 所示,很多在前面的示例中已经有所涉及。表 3.7 Gcc 总体选项列表 后 缀 名 所对应的语言-c 只是编译不链接,生成
37、目标文件“.o”-S 只是编译不汇编,生成汇编代码-E 只进行预编译,不做其他处理-g 在可执行程序中包含标准调试信息-o file 把输出文件输出到 file 里-v 打印出编译器内部编译各过程的命令行信息和编译器的版本-I dir 在头文件的搜索路径列表中添加 dir 目录-L dir 在库文件的搜索路径列表中添加 dir 目录-static 链接静态库-llibrary 连接名为 library的库文件 对于“-c”、“-E”、“-o”、“-S”选项在前一小节中已经讲解了其使用方法,在此主要讲解另外两个非常常用的库依赖选项“-I dir”和“-L dir”。“-I dir”正如上表中所述
38、,“-I dir”选项可以在头文件的搜索路径列表中添加 dir 目录。由于 Linux中头文件都默认放到了“/usr/include/”目录下,因此,当用户希望添加放置在其他位置的头文件时,就可以通过“-I dir”选项来指定,这样,Gcc 就会到相应的位置查找对应的目录。比如在“/root/workplace/Gcc”下有两个文件:/*hello1.c*/#include int main()printf(Hello!n);return 0;/*my.h*/#include QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 这样,就可在 Gcc 命令
39、行中加入“-I”选项:rootlocalhost Gcc Gcc hello1.c I/root/workplace/Gcc/-o hello1 这样,Gcc 就能够执行出正确结果。小知识 在 include 语句中,“”表示在标准路径中搜索头文件,“”表示在本目录中搜索。故在上例中,可把 hello1.c 的“#include”改为“#include“my.h”,就不需要加上“-I”选项了。“-L dir”选项“-L dir”的功能与“-I dir”类似,能够在库文件的搜索路径列表中添加 dir 目录。例如有程序 hello_sq.c 需要用到目录“/root/workplace/Gcc/l
40、ib”下的一个动态库 libsunq.so,则只需键入如下命令即可:rootlocalhost Gcc Gcc hello_sq.c L/root/workplace/Gcc/lib lsunq o hello_sq 需要注意的是,“-I dir”和“-L dir”都只是指定了路径,而没有指定文件,因此不能在路径中包含文件名。另外值得详细解释一下的是“-l”选项,它指示 Gcc 去连接库文件 libsunq.so。由于在 Linux下的库文件命名时有一个规定:必须以 l、i、b 3 个字母开头。因此在用-l 选项指定链接的库文件名时可以省去 l、i、b 3 个字母。也就是说 Gcc 在对“-l
41、sunq”进行处理时,会自动去链接名为 libsunq.so 的文件。(2)告警和出错选项 Gcc 的告警和出错选项如表 3.8 所示。表 3.8 Gcc 总体选项列表 选 项 含 义-ansi 支持符合 ANSI标准的 C 程序-pedantic 允许发出 ANSI C 标准所列的全部警告信息 续表 选 项 含 义-pedantic-error 允许发出 ANSI C 标准所列的全部错误信息-w 关闭所有告警-Wall 允许发出 Gcc 提供的所有有用的报警信息-werror 把所有的告警信息转化为错误信息,并在告警发生时终止编译过程 下面结合实例对这几个告警和出错选项进行简单的讲解。如有以
42、下程序段:#include void main()QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解 第 3 章、Linux 下的 C 编程基础 华清远见培训教材 long long tmp=1;printf(This is a bad code!n);return 0;这是一个很糟糕的程序,读者可以考虑一下有哪些问题?“-ansi”该选项强制 Gcc 生成标准语法所要求的告警信息,尽管这还并不能保证所有没有警告的程序都是符合 ANSI C 标准的。运行结果如下所示:rootlocalhost Gcc#Gcc ansi warning.c o warning warni
43、ng.c:在函数“main”中:warning.c:7 警告:在无返回值的函数中,“return”带返回值 warning.c:4 警告:“main”的返回类型不是“int”可以看出,该选项并没有发现“long long”这个无效数据类型的错误。“-pedantic”允许发出 ANSI C 标准所列的全部警告信息,同样也保证所有没有警告的程序都是符合ANSI C 标准的。其运行结果如下所示:rootlocalhost Gcc#Gcc pedantic warning.c o warning warning.c:在函数“main”中:warning.c:5 警告:ISO C90 不支持“long
44、 long”warning.c:7 警告:在无返回值的函数中,“return”带返回值 warning.c:4 警告:“main”的返回类型不是“int”可以看出,使用该选项查看出了“long long”这个无效数据类型的错误。“-Wall”允许发出 Gcc 能够提供的所有有用的报警信息。该选项的运行结果如下所示:rootlocalhost Gcc#Gcc Wall warning.c o warning warning.c:4 警告:“main”的返回类型不是“int”warning.c:在函数“main”中:warning.c:7 警告:在无返回值的函数中,“return”带返回值 war
45、ning.c:5 警告:未使用的变量“tmp”使用“-Wall”选项找出了未使用的变量 tmp,但它并没有找出无效数据类型的错误。另外,Gcc 还可以利用选项对单独的常见错误分别指定警告,有关具体选项的含义感兴趣的读者可以查看 Gcc 手册进行学习。(3)优化选项 Gcc 可以对代码进行优化,它通过编译选项“-On”来控制优化代码的生成,其中 n 是一个代表优化级别的整数。对于不同版本的 Gcc 来讲,n 的取值范围及其对应的优化效果可能QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 并不完全相同,比较典型的范围是从 0 变化到 2 或 3。不同的优
46、化级别对应不同的优化处理工作。如使用优化选项“-O”主要进行线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。使用优化选项“-O2”除了完成所有“-O1”级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。选项“-O3”则还包括循环展开和其他一些与处理器特性相关的优化工作。虽然优化选项可以加速代码的运行速度,但对于调试而言将是一个很大的挑战。因为代码在经过优化之后,原先在源程序中声明和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环语句也有可能因为循环展开而变得到处都有,所有这些对调试来讲都将是一场噩梦。所以笔者建
47、议在调试的时候最好不使用任何优化选项,只有当程序在最终发行的时候才考虑对其进行优化。(4)体系结构相关选项 Gcc 的体系结构相关选项如表 3.9 所示。表 3.9 Gcc 体系结构相关选项列表 选 项 含 义-mcpu=type 针对不同的 CPU 使用相应的 CPU 指令。可选择的 type 有 i386、i486、pentium 及 i686 等-mieee-fp 使用 IEEE 标准进行浮点数的比较-mno-ieee-fp 不使用 IEEE 标准进行浮点数的比较-msoft-float 输出包含浮点库调用的目标代码-mshort 把 int 类型作为 16 位处理,相当于 short
48、int-mrtd 强行将函数参数个数固定的函数用 ret NUM 返回,节省调用函数的一条指令 这些体系结构相关选项在嵌入式的设计中会有较多的应用,读者需根据不同体系结构将对应的选项进行组合处理。在本书后面涉及到具体实例会有针对性的讲解。3.5 Gdb 调试器 调试是所有程序员都会面临的问题。如何提高程序员的调试效率,更好更快地定位程序中的问题从而加快程序开发的进度,是大家共同面对的。就如读者熟知的 Windows 下的一些调试工具,如 VC 自带的如设置断点、单步跟踪等,都受到了广大用户的赞赏。那么,在 Linux下有什么很好的调试工具呢?本文所介绍的 Gdb 调试器是一款 GNU 开发组织
49、并发布的 UNIX/Linux 下的程序调试工具。虽然,它没有图形化的友好界面,但是它强大的功能也足以与微软的 VC 工具等媲美。下面就请跟随笔者一步步学习 Gdb 调试器。3.5.1 Gdb 使用流程 这里给出了一个短小的程序,由此带领读者熟悉一下 Gdb 的使用流程。建议读者能够实际动手操作。首先,打开 Linux 下的编辑器 Vi 或者 Emacs,编辑如下代码(由于为了更好地熟悉 GdbQQ:313638714http:/ 嵌入式 Linux 应用程序开发详解 第 3 章、Linux 下的 C 编程基础 华清远见培训教材 的操作,笔者在此使用 Vi 编辑,希望读者能够参见 3.3 节中
50、对 Vi 的介绍,并熟练使用 Vi)。/*test.c*/#include int sum(int m);int main()int i,n=0;sum(50);for(i=1;i=50;i+)n+=i;printf(The sum of 1-50 is%d n,n);int sum(int m)int i,n=0;for(i=1;i=m;i+)n+=i;printf(The sum of 1-m is%dn,n);在保存退出后首先使用 Gcc 对 test.c 进行编译,注意一定要加上选项“-g”,这样编译出的可执行代码中才包含调试信息,否则之后 Gdb 无法载入该可执行文件。rootloc