理论教育 使用GCC编译运行程序

使用GCC编译运行程序

时间:2023-11-25 理论教育 版权反馈
【摘要】:表5.1GCC 常用参数说明表续表3. 编译执行中的常见错误不同于运行阶段发生的结果不正确的逻辑错误, 编译阶段的报错主要是语法错误, 多数是由于编译器不能识别变量或相关功能函数, 或者书写语法有误。2011 年, ISO/IEC 9899: 2011 发布, 简称C11 标准。

使用GCC编译运行程序

1.GCC 工作流程

GCC 通过文件的后缀来区别输入文件的类型, 常见的文件及后缀名如下所示。

.c——C 语言源代码文件。

.h——程序所包含的头文件。

.o——编译后的目标文件。

.C、 .cpp、 .cc 或.cxx——C++源代码文件。

ii——已经预处理过的C++源代码文件。

.i——已经预处理过的C 源代码文件。

.s——汇编语言源代码文件。

.S——经过预编译的汇编语言源代码文件。

a——由目标文件构成的档案库文件。

在真正编译执行一个程序前, 我们先来了解一下一个C 语言程序编译的过程, 体验GCC 如何分别调用预处理、 编译、 链接工具, 自动完成程序的编译工作。

(1) 预处理。

用户程序中, 除了自己的功能代码, 难免要调用一些程序库里的过程或一些已定义的宏, 这些已定义内容的代码是包含在相关头文件中的, 如printf 的定义代码在stdio.h 中。预处理阶段就是加载头文件和展开宏, 这一步操作仅对文本信息处理, 是编译前的准备、整理工作。

(2) 编译。

编译器就像一个了解C 语言和机器语言两种语言的翻译, 它将C 语言代码转换为对应计算机的指令码, 形成的二进制指令的集合就是编译的结果文件, 是二进制文件。 但若程序不仅有主程序模块, 还有多个子程序模块, 此时因为各模块还相对独立没有成为一个整体, 还不能付诸执行。

(3) 链接。

处理多个模块的指令地址, 使其合成一个可执行的整体。

(4) 装入执行。

将程序指令装入内存执行。 出于系统内存利用率和效率的考虑, 现代操作系统链接程序时有静态链接和动态链接两种方式。 一般程序自己的多个模块往往采用静态链接的方式, 在装入内存前完成链接成一体的工作, 而系统库的功能调用则常采用动态链接的方式, 在程序装入时动态的加载。 动态链接的方式好处是被调用库里的功能代码发生变化时, 用户程序不需要重新编译, 只是在装入内存执行时动态地链接新编译的库即可。

2. 编译执行第一个程序

【例5-1】 hellowworld 程序的编译执行, 编辑源代码文件: vi hello.c

编译执行:

执行代码:

由于编译得到的程序所在目录并不在系统的PATH 环境变量里, 所以执行该程序时要给清楚程序所在目录, 比如在当前目录下应执行如下命令, 代表当前目录的点符号不能丢。

GCC 常用参数见表5.1。(www.daowen.com)

表5.1 GCC 常用参数说明表

续表

3. 编译执行中的常见错误

不同于运行阶段发生的结果不正确的逻辑错误, 编译阶段的报错主要是语法错误, 多数是由于编译器不能识别变量或相关功能函数, 或者书写语法有误。 常见的报错原因如下。

(1) 语法错误。

错误信息举例: 文件source.c 中第n 行有语法错误(syntex error)。

此类错误主要是程序作者所写的标识符、 表达式等书写不符合语法规范导致的。 比如, 变量未声明就直接使用在C 语言中是不符合规范的(其他语言有允许不声明直接使用变量)。 编译器报错会比较友好的提示错误所在的位置行及错误说明。

(2) 头文件错误。

错误信息: 找不到头文件head.h , 如提示: “can not find include file head.h”。

头文件中的内容主要是声明函数的接口, 是编译时必需的, 如果找不到头文件, 还会导致函数名不识别。 GCC 寻找所需的头文件会先从-I 命令参数所给的目录开始, 如果没有指定该参数, 就找环境变量如C_INCLUDE_PATH、 CPLUS_INCLUDE_PATH、 OBJC_INCLUDE_PATH 等目录下, 再找不到就找/usr 下的一些内定目录。

(3) 函数库错误。

错误信息: 链接程序找不到所需的函数库。 如提示: “ld: cannot find crt1.o: No such file or directory”。

Linux 系统和Windows 系统下的库都有静态编译、 动态编译之分, Linux 系统下动态库和静态库分别是.so 和.a 文件, Windows 下则是.dll 和.lib 文件。 静态库文件必须在编译期就连接到应用程序中, 调用的库函数的索引和实现都在其中。 静态编译的好处是结果程序运行时不需要再挂动态库; 缺点就是应用程序比较大, 失去了动态库的灵活性, 程序修改后要重新编译发布。

与静态库不同的是, 动态链接文件是运行期才被调用的动态库, 应用程序编译链接时, 库中的函数和数据并不复制到可执行文件中, 而是通过静态库的说明编译, 装入时再把程序代码和动态库文件里被调用的函数代码链接起来, 从而节省了内存资源。 两种库文件应随应用程序一起发行, 否则应用程序运行时会产生错误。

静态编译阶段或执行阶段, 如果GCC 找不到需要的静/动态库文件也会报错。 一般寻找库文件时, 也是先找命令参数-L 指定的搜索目录, 再找GCC 的环境变量LIBRARY_PATH , 再找内定目录/lib /usr/lib /usr/local/lib 等。

【例5-2】 引入自定义头文件, 设fd 目录下有testf.c 程序, 调用子目录fdhead 下自定义的math.h 中的函数或过程实现功能。

①~/fd/testf.c 源代码。

②~/fd/fdhead/math.h 源代码。

③编译执行命令。

◎C 语言版本问题

C 语言最初于1969—1973 年由Dennis Ritchie 在AT&T 贝尔实验室里开发, 主要用于重新实现UNIX 操作系统。 1989 年, 美国国家标准协会(ANSI)对C 语言进行标准化, 制定了ANSI C, 而后ANSI C 被国际标准化组织ISO 采纳为ISO/IEC 9899:1990, 这个最初的标准常被称为C89 或C90。 随后, C 语言的标准化委员会不断对C语言进行改进, 1999 年正式发布ISO/IEC 9899:1999, 简称C99 标准。 2011 年, ISO/IEC 9899: 2011 发布, 简称C11 标准。

所以, C 语言程序的编写可能在语法上会有符合不同标准规范的写法, 而不同编译器对各标准的支持程度不同。 GCC 编译器对C99 支持高达90%以上, 还自行扩展了如零长度数组、 属性声明等。 微软主要是C++编译器, 标准基于ANSI C 不断发展,也有自己变化。

所以程序能顺利编译, 受语言规范和编译器支持两个因素影响, 如果编程采用了一些新规范引入的特性语法, 要考虑编译器是否支持。 本书所附的程序均在GCC 9.1.0 下编译测试通过。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈