当前位置: 首页 > article >正文

c语言程序的生命周期(编写、预处理、编译、汇编、链接、执行)

目录

    • 程序的生命周期
      • 一、引言
      • 二、程序经历的六个阶段
        • 1、阶段一:编辑(Edit)
        • 2、程序编译系统(Programs Compilation System)
          • 阶段二:预处理(Preprocess)
          • 阶段三:编译(Compile)
          • 阶段四:汇编(Assemble)
          • 阶段五:链接(Link)
        • 3、阶段六:执行(Execute)

程序的生命周期

一、引言

什么是计算机系统(Computer System)?

计算机系统是一起工作以运行计算机程序(Computer Program)的硬件和软件组件(hardware and software components)的集合

什么是计算机程序(Computer Program)

计算机程序是针对计算机的指令(instructions)

计算机程序是如何在计算机系统上运行的?
在这里插入图片描述

二、程序经历的六个阶段

  1. 编辑(Edit)
  2. 预处理(Preprocess)
  3. 编译(Compile)
  4. 汇编(Assemble)
  5. 链接(Link)
  6. 执行(Execute)
1、阶段一:编辑(Edit)

在编辑器中编写代码

如下所示:hello.c

//hello.c
#include<stdio.h>
int main(){printf("hello,world!\n");
}
2、程序编译系统(Programs Compilation System)

如图所示:
在这里插入图片描述

阶段二:预处理(Preprocess)

在源代码编译之前对其进行一些文本性质的操作。它的主要任务包括:

1、删除注释
2、插入被#include指令包含的文件的内容
3、定义和替换由#define指令定义的符号
4、确定代码的部分内容是否应该根据一些条件编译指令进行编译

条件编译简单语法形式如下:

#if constant-expressionstatements
#endif

constant-expression :(常量表达式)由预处理器求值,如果值为非零(真),那么statements部分就被正常编译,否则预处理器就安静地删除它们。

条件语句常用于调试:例如,将你所有调试代码都以下面这种形式出现:

#if DEBUGprintf("x=%d,y=%d\n",x,y);
#endif

这样,不管我们想编译还是忽略这个代码都很容易做到。
如果想编译它,只要使用:

#define DEBUG 1

如果想忽然它,只要使用:

#define DEBUG 0

使用命令gcc -E hello.c > hello.i生成相应的hello.i文件
文章最后会贴hello.i文件,属实有点长了

阶段三:编译(Compile)

按顺序分为三个过程:语句分析、中间代码生成、代码优化
语句分析
对整体代码进行扫描处理,对代码进行词法分析,语法分析,语义分析,以排除不合法的、不规范的代码。

词法分析:关键字的正确性、标识符的有效性、立即数的展开。
语法分析:变量命名规范、程序结构合法性、函数定义的正确性、重定义现象等
语义分析:表达式的合理性、变量未初始化使用等。

中间代码生成

中间代码生成是产生中间代码的过程。所谓“中间代码”是一种结构简单、含义明确的记号系统,这种记号系统复杂性介于源程序语言和机器语言之间,容易将它翻译成目标代码。
另外,还可以在中间代码一级进行与机器无关的优化。

使用命令gcc -fdump-tree-all hello.c,然后查看.cfg文件,就可以看到生成的中间文件了
hello.c.013t.cfg:

;; Function main (main)main ()
{
<bb 2>:__builtin_puts (&"hello,world!"[0]);return;}

代码优化

代码优化是指对中间代码进行等价(指不改变程序的运行结果)变换
等价的含义是使得变换后的代码运行结果与变换前代码运行结果相同。
优化的含义是最终生成的目标代码短(运行时间更短、占用空间更小),时空效率优化。

使用命令gcc -S hello.c生成hello.s文件(汇编语言)
hello.s:

	.file	"hello.c".def	___main;	.scl	2;	.type	32;	.endef.section .rdata,"dr"
LC0:.ascii "hello,world!\0".text.globl	_main.def	_main;	.scl	2;	.type	32;	.endef
_main:
LFB6:.cfi_startprocpushl	%ebp.cfi_def_cfa_offset 8.cfi_offset 5, -8movl	%esp, %ebp.cfi_def_cfa_register 5andl	$-16, %espsubl	$16, %espcall	___mainmovl	$LC0, (%esp)call	_putsleave.cfi_restore 5.cfi_def_cfa 4, 4ret.cfi_endproc
LFE6:.def	_puts;	.scl	2;	.type	32;	.endef

编译时将变量名替换为内存地址,变量名编译完之后就不存在了
这里说的变量的内存地址是虚拟内存地址。这里的虚拟内存地址是固定的,而物理内存地址是随机的,内存使用时,才将虚拟内存映射到物理内存页

阶段四:汇编(Assemble)

针对生成的汇编代码,将其逐句解析转换生成一一对应的机器码(二进制编码)。
注意这个阶段之后才生成了二进制文件。

阶段五:链接(Link)

为什么要链接?

一个项目中有很多源文件和依赖库
源文件之间存在依赖关系
每个源文件都是独立编译的,即每个.c文件会形成一个.o文件
综上:为了实现依赖关系,需要将该.o文件中依赖的其他.o文件导入进来,这便是链接。

链接分为:静态链接和动态链接
静态链接

静态链接是以目标文件为单位的,如hello.c中,我们需要引用了静态库中的printf()函数,那么链接器就会把库中包含printf()函数的那整个目标文件都链接进来。

如果我们有几个.c文件中都用到了printf()函数

这几个.c文件都会把库中包含printf()函数的那整个目标文件都链接进来。

缺点:

那么同一份目标文件就被复制了好几份,占用的空间大。
同时,如果这个目标文件需要更新,需要重新进行编译链接形成可执行程序

优点:

优点就是,程序执行的时候已经包含了所有的函数,速度快。

动态链接

动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。
操作系统负责将需要的动态库加载到内存中,然后程序在运行到指定的代码时,去共享执行内存中已经加载的动态库可执行代码,最终达到运行时连接的目的。

如果我们有几个.c文件中都用到了printf()函数

第一个.c文件会把库中包含printf()函数的那个目标文件加载到内存中。
当其他.c文件发现那个目标文件已经加载到内存中了,就不需要再加载
而是将内存中已经存在的目标文件映射到其他.c文件的虚拟地址空间中,从而进行链接

优点:

所有程序共享一份副本,文件体积小
更新比较方便,更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。

缺点:

因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失

该阶段重定位时,涉及地址的改变

3、阶段六:执行(Execute)

程序如何执行?
1、hello.exe存储在硬盘中
如图所示:
在这里插入图片描述

2、从键盘中读取hello命令
如图所示:
在这里插入图片描述

3、将hello.exe从硬盘载入内存
如图所示:
在这里插入图片描述

4、从内存到显示器写这个输出的字符串
如图所示:
在这里插入图片描述
关于hello.i文件

# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "hello.c"# 1 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/stdio.h" 1 3
# 19 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/stdio.h" 3
# 1 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/_mingw.h" 1 3
# 32 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/_mingw.h" 3# 33 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/_mingw.h" 3
# 20 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/stdio.h" 2 3# 1 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/include/stddef.h" 1 3 4
# 212 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/include/stddef.h" 3 4
typedef unsigned int size_t;
# 324 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/include/stddef.h" 3 4
typedef short unsigned int wchar_t;
# 353 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/include/stddef.h" 3 4
typedef short unsigned int wint_t;
# 27 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/stdio.h" 2 3# 1 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/include/stdarg.h" 1 3 4
# 40 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/include/stdarg.h" 3 4
typedef __builtin_va_list __gnuc_va_list;
# 29 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/stdio.h" 2 3
# 129 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/stdio.h" 3
typedef struct _iobuf
{char* _ptr;int _cnt;char* _base;int _flag;int _file;int _charbuf;int _bufsiz;char* _tmpfname;
} FILE;
# 154 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/stdio.h" 3
extern __attribute__ ((__dllimport__)) FILE _iob[];
# 169 "e:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/stdio.h" 3FILE* __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fopen (const char*, const char*);FILE* __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) freopen (const char*, const char*, FILE*);int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fflush (FILE*);int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fclose (FILE*);int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) remove (const char*);int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) rename (const char*, const char*);FILE* __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) tmpfile (void);char* __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) tmpnam (char*);char* __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) _tempnam (const char*, const char*);int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) _rmtmp(void);int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) _unlink (const char*);char* __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) tempnam (const char*, const char*);int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) rmtmp(void);int __attribute__((__cdecl__
http://www.lryc.cn/news/2419780.html

相关文章:

  • 四个数列(二分查找)
  • IoU,GIoU,DIoU、CIoU详解
  • System.ArgumentException HResult=-2147024809 Message=参数无效。 Source=System.Drawing
  • 标志位寄存器与CF、OF标志位的区分
  • 史上可以针对大部分对于鼠标右键菜单的设置
  • 常用协议对应的端口号
  • Javaweb开发项目之JS知识(JavaScript)
  • 日本推出罩杯测量APP,罩杯大小一夹便知!
  • AFL实战
  • 中国家装水管十大品牌排行榜:联塑、日丰、金牛、弗锐德、美尔固等品牌上榜
  • 字体下载_ps字体打包下载,送你1.15G+316款可用字体
  • 8005端口导致的阿里云上的tomcat无法外部访问
  • 2021-09-18堡垒机
  • SuperMap iMobile for Android许可介绍
  • Phoenix 的 thick Client 和 thin Client
  • Actix-Web构建一个简单的HTTP服务器
  • 51单片机原理以及接口技术(四)--80C51的程序设计
  • greensock下载_GreenSock动画平台初学者指南
  • 手把手叫你做ToDoList
  • 解密:2012世界末日其实是个大骗局
  • 算法设计与分析——背诵知识点合集
  • 霍夫曼(Huffman)编码算法详解之C语言版
  • 强度理论介绍和惯性矩推导
  • 数据库性能监控策略:如何监控数据库性能
  • 基本概念:子域名和域
  • 【HTML基础】HTML基本语法
  • 【CSDN软件工程师能力认证学习精选】吐血整理!140 种 Python 标准库、第三方库和外部工具都有了
  • linux驱动开发扩展--字符设备注册详解
  • 多线程之线程间通讯
  • (4)pokeman_用图片对模型进行测试