C语言程序从编写到运行历经的几个阶段
C语言程序从编写到运行历经的几个阶段
一 前言
在完成 .c 或 .cpp 文件的编写后,我们通常直接 gcc 或 g++ 后接文件名,就可以在当前文件夹下生成 a.out 可执行文件, 之后输入 ./a.out 即可执行该二进制可执行文件。
但实际上C语言程序从编写到运行,这期间的经历并不是这么简单,那么现在小编就带领大家探索,这期间具体有哪几个步骤?
一 过程简介
从上图可知从C源码到可执行程序,我们会历经三个步骤,分别是:预处理阶段、编译阶段以及最后的链接阶段。但是如果我们分的更细一点,其实我们可以分成四个步骤:
显然由图片我们可以知道经历的四个步骤是:预处理、编译、汇编、链接
通常gcc命令后面不加选项的话,就会默认执行预处理、编译、汇编、链接所有步骤,若程序没有错误的话,我们就可以得到一个可执行文件,默认为 a.out, 这也是小编在前言中说的。
-E选项:编译器执行完预处理阶段就停止执行,后面的编译、汇编等操作就不会执行。
-S选项:编译器执行完编译阶段就会停止。
-c选项:编译器执行完汇编阶段就会停止。
其实,这三个阶段只是限定了编译器执行操作的截止时间,而不是单独的将某一步拎出来执行。
二 预处理阶段
执行 gcc -E hello.c > hello.i 命令后,我们仅仅执行预编译操作,生成一个.i 文件 (这个文件是我们最后还可以读得懂的文件了,我们可以打开这个文件,仔细观察程序出现了哪些变化)
那么预处理阶段都进行了哪些操作呢?
- 对所有以 # 开头的语句进行处理,其中包括我们熟知的:#define、#include <xxx.h>、条件编译指令#ifdef等。
- 删除所有的“/**/”和“//”注释。
- 添加行号和文件名标识,方便编译器产生的调用以及当出现编译错误或者是警告时可以显示行号。
- 保留所有的 #pragma 编译指令
这里小编想着重阐述的是第一部分!!!
程序中以“#include”开头的语句都会被替换成相应头文件中的内容 (也就是说,项目中不论是自己写的 被#include ""引用的.h 头文件还是系统自带的#include <> 头文件,在预处理阶段阶段之后都会消失,并且这个过程是递归进行的,因为被包含的文件还有可能包含了其他文件,同时为了避免头文件的重复包含,我们引入了#ifdef,#ifndef等条件编译指令,这里就不细说)。
此外程序中的#define 定义的宏在使用的地方都会进行替换 (大家不要小看宏定义,这决不仅是 #define PI 3.14之后进行使用这么简单)
关于宏定义的其他操作如下:
还有系统已经定义好的宏,我们可以直接拿过来使用
在这里小编想强调两点:
- 宏 只是替换
例如我们上图中定义: #define S(a, b) a * b,这显然是用来求乘积的,那我们现在在程序中调用它最终的结果是什么呢?
例如:S(5, 3 + 1),最后的结果会是: 5 * 4 = 20 吗?
显然不是,这条语句会被替换成:5 * 3 + 1,所以最终的答案是 16 !!!
同时也因为只是替换,所以宏替换不会占用程序的运行时间。 - 可以通过宏 定义代码段
在这里大家可以粗略的理解成 “宏可以产生代码”。
至于每一行语句的最后需要加上反斜杠,这是因为宏定义只可以出现在一行,所以我们才使用 '\'进行连接。
三 编译阶段
使用 -S选项,编译器执行完编译阶段就结束,最后形成 .s 文件
应该说编译阶段是整个程序从C到机器语言翻译过程的核心,我们之前学习的编译原理这门课中讲到的词法分析、语法分析、语义分析以及之后的优化等其他操作, 其实就是在这个阶段执行的。
四 汇编阶段
使用 -c 选项,编译器执行完汇编阶段就结束,形成 .o (windows下为 .obj ) 对象文件。
其中汇编器将会汇编代码转换为机器可识别的机器代码,之前项目中有几个 source.c 文件,此时就会出现几个对象文件
五 链接阶段
前一个阶段我们得到了若干个对象文件,现在我们要做的就是将这几个对象文件链接起来,形成最后的可执行文件。
(这其中还涉及到静态链接库和动态链接库的概念,若想了解,请点击我)
(20220311更新) 静态链接库和动态链接库代码演示:
静态链接和动态链接的区别在于:对函数库的链接是在编译期完成还是程序运行时期完成,若是编译期间则是静态链接,对应的函数库就是静态链接库 (linux下为.a文件,win下为.lib文件),若是在运行时期完成,则为动态链接,对应的函数库就是动态链接库 (linux下为.so文件,win下为.dll文件),不论静态链接库还是动态链接库,都是由若干个对象文件构成的。
闲话不多说,我们先写一些演示代码 (先演示静态链接库后演示动态链接库),如下分别创建:add.h、add.cpp、sub.h、sub.cpp、main.cpp 五个文件,文件内容如下:
/* sub.h 文件内容 */
#ifndef _SUB_H
#define _SUB_H
int sub(int, int);
#endif/* sub.cpp 文件内容 */
#include "sub.h"int sub(int x, int y) {return x - y;
}/* add.h 文件内容 */
#ifndef _ADD_H
#define _ADD_H
int add(int, int);
#endif/* add.cpp 文件内容 */
#include "add.h"int add(int x, int y) {return x + y;
}/* main.cpp 文件内容 */
#include "sub.h"
#include "add.h"
#include <iostream>
using namespace std;int main() {cout << "1 + 2 = " << add(1, 2) << endl;cout << "1 - 1 = " << sub(1, 2) << endl;return 0;
}
(1) 编译和使用静态链接库过程如下:
大致过程为:将 add.cpp 和 sub.cpp 文件分别编译为对象文件,然后将其打包为静态链接库,之后再和main.cpp一块编译获得可执行文件。
(2) 编译和使用动态链接库过程如下:
大致过程为:将 add.cpp 和 sub.cpp 文件分别编译为对象文件,然后将其打包为动态链接库 (.so文件) ,若没有使用sudo cp libmymath.so /lib
却直接运行./main
可执行文件,将会报错,显示找不到动态链接库No such file or directory
,这是因为默认情况下,编译器只会使用/lib和/usr/lib这两个目录下的库文件。故此处将.so文件添加到了/usr/lib
目录下。
至此,我们的阐述就结束了。加油,路漫漫其修远兮,吾将上下而求索,与君共勉!!!
C语言程序从编写到运行历经的几个阶段相关推荐
- 在C语言上如何编写并运行MPI程序
一.安装MPI 第一步:下载MPI软件包 得到文件: mpich.nt. 1.2.5.zip 第二步:安装 1.解压缩文件mpich.nt.1.2.5.zip到目录mpich.nt.1.2.5 2.进 ...
- Ubuntu下C语言程序的编写
Ubuntu下C语言程序的编写 要求:请编写一个主程序文件 main1.c 和一个子程序文件 sub1.c, 要求:子程序sub1.c 包含一个算术运算函数 float x2x(int a,int b ...
- 零基础学习PHP编程——程序的编写和运行过程
零基础学习PHP编程--程序的编写和运行过程 注意: 本文主要写给零基础的同学,作为编程的入门引导, 如有不当之处,还请指正. 访问源站 欢迎交流QQ群: 640765823 回顾上一节,我们已经基本 ...
- C语言 程序代码编写规范
前言 一个好的程序编写规范是编写高质量程序的保证.清晰.规范的源程序不仅仅是方便阅读,更重要的是能够便于检查错误,提高调试效率,从而最终保证软件的质量和可维护性. 说明 l 本文档主要适用于刚刚开始接 ...
- c语言程序前言,C语言 程序代码编写规范前言
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 一个好的程序编写规范是编写高质量程序的保证.清晰.规范的源程序不仅仅是方便阅读,更重要的是能够便于检查错误,提高调试效率,从而最终保证软件的质量和可维护性 ...
- c语言程序报告的前言,C语言 程序代码编写规范前言
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 一个好的程序编写规范是编写高质量程序的保证.清晰.规范的源程序不仅仅是方便阅读,更重要的是能够便于检查错误,提高调试效率,从而最终保证软件的质量和可维护性 ...
- Java程序从编写到运行
Java程序从开发到运行包括编译和运行两个阶段,这两个阶段可以在不同的操作系统中完成,例如在windows环境下进行编译,在Linux环境下运行,这是因为有JVM机制的存在,实现了Ja ...
- C语言——C语言程序的编写
第一讲.C语言程序的编写 一.开发工具的选择 1)可以用来写代码的工具,记事本.ULtraEdit.Vim.Xcode等 2)选择Xcode的原因:苹果官方提供的开发利器.简化开发过程.有高亮显示功能 ...
- Ubuntu下bpf纯c程序的编写与运行
1. 前言 搭建bpf运行环境真的是很费时间,踩过的坑也不少,今天也是因为虚拟机扩容崩了,重新安装了最新的ubuntu,bpf运行环境得以搭建完成,也算是破而后立.现在将bpf运行环境搭建和bpf纯c ...
最新文章
- 无监督学习之RBM和AutoEncoder
- 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 一 )
- Android Studio 快捷键使用说明
- Kibana查询说明
- @RequestParam和@RequestBody的区别 (结合 Get/Post )
- 【已解决】Jsp实现文件上传功能
- 【数据结构与算法-2】链表
- JUnit 4和JUnit 5区别
- Win10 第三方浏览器无法上网或者上网速度慢 的终极解决方案
- 时间格式转化大汇总各种类型
- CentOS7.6Arm Qt开发环境(GCC+Qt5+QtCreator+JDK8+MySQL8+CMake+ProtoBuf+Python+SVN)
- 一句话木马上传常见的几种方法
- Mblog 系统配置
- 火狐浏览器设置关闭提醒
- 一位计算机牛人的心得,谈到计算机和数学,很实用~
- 【Tensorflow】卷积层
- 1分钟推荐一个深度学习领域的必备网站,高能!
- 前端求职刷题(第一部分)
- 学会这5招优化技巧,让你的 iPhone 变得更好
- 简单的奥特曼打小怪兽
热门文章
- 通过更改字体解决华三H3C模拟器HCL末行只显示半行字的问题
- Uncaught TypeError: Converting circular structure to JSON
- com.mysql.cj.exceptions.ConnectionIsClosedException: No operations allowed after connection closed.
- java 页码计算_分页之页面页码列表计算
- 微信小程序开发工具下载以及安装教程
- Kettle (3) - 连接 SAP Hana 数据库
- c++Primer5,总览与IO库和泛型算法
- 振动、位移、键相、转速、温度探头的安装要求及步骤
- 中兴oltc320用户手册_中兴C320C300V2版本OLT开局配置手册.doc-资源下载在线文库www.lddoc.cn...
- apache commons 系列学习