从源代码到EXE

本文详细介绍了程序从源代码到可执行文件的全过程,包括预处理、编译、汇编和链接四个阶段。预处理阶段处理头文件、宏替换和条件编译;编译阶段进行词法、语法、语义分析,并生成中间语言和优化目标代码;汇编阶段将汇编代码转换为机器代码;链接阶段整合目标文件和库,生成最终的可执行EXE文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

程序运行始末

1.编译器将组成程序的每一个源代码(.c)文件经过编译器complier分别编译为目标代码(Windows ->.obj;类Unix->.o),然后由链接器linker包含头文件(用户自定义头文件.h 或.hpp与引用库里的头文件.h)和所有目标代码生成一个单一的可执行文件(.exe).执行exe文件时,由操作系统将文件内容加载进内存,
预处理prepressing  编译compilation 汇编assembly 链接  linking 
GCC命令选项:ESC     文件后缀:ISO
编译过程分为预处理阶段、解析源代码、生成目标代码。

一、预处理阶段\预编译:  gcc  -E test.c -o test.i

  1. #include头文件的包括
  2. 删除所有的#define并完成宏的替换
  3. 清除注释(替换为空格)
  4. 处理所有条件编译指令
  5. 添加行号和文件名标识
  6. 保留#pragma编译器指令给编译器使用。结束生成 .i (.ii  C++)文件.

ps:防止多次编译:

1.pragma once

#pragma once用来防止同一文件被多次include, #pragma once是编译相关,这里的同一文件指的是物理文件。
弊端:如果某一个头文件有多份拷贝,那么这些文件虽然在逻辑上都是一样的,但是在物理上他们却是不同的,所以把这些文件包含的时候,就会发现全部都包含进来了,然后就是编译错误了。

2.宏

#define   HEAD_H
#endif 

……(头文件内容) 

#ifndef   HEAD_H

原理:条件编译
弊端:
#ifndef最早期使用的方法,是基于语言的宏定义名字不能冲突的前提下的。如果#ifndef 后面的宏名和程序中的其他宏名”撞车“会发生报错。

不发生错误下二者区别:
当物理上的同一文件被嵌套包含的时候,使用第一种方法预处理会每一次打开该文件做判断的,但是第二种方法则不会,所以在此#pragma once 会更快些。
ANSI 标准 C 还定义了如下几个宏:
_LINE_ 表示正在编译的文件的行号
_FILE_ 表示正在编译的文件的名字
_DATE_ 表示编译时刻的日期字符串,例如: "25 Dec 2007"
_TIME_ 表示编译时刻的时间字符串,例如: "12:30:55"
_STDC_ 判断该文件是不是定义成标准 C 程序
如果编译器不是标准的,则可能仅支持以上宏的一部分,或根本不支持。当然编译器也有可能还提供其它预定义的宏名。注意:C标准宏名的书写由标识符与两边各二条下划线构成。

二、编译:   gcc  -S test.i -o test.s

          将预处理完的文件进行一系列的扫描,词法分析,语法分析,语义分析,生成汇编代码及优化后产生相应的汇编代码文件。

1.词法分析:

源代码被输入到扫描器scanner 中,scanner通过类似于有限状态机的算法将源代码的字符分割为一系列的记号。 
词法分析产生的记号分为关键字 标识符 字面量(数字 字符串)和特殊符号(+  -   =).识别记号的同时,扫描器也将标识符存到符号表中,将数字字符串常量存放到文件表中已被后面的步骤使用。

2.语法分析

grammer parser语法分析器:完成表达式层面的分析
语法分析工具依据用户给定的语言规则构建 语法树(以表达式为节点的树)。

3.语义分析:

semantic Analyzer语义分析器
对语法树进一步完善。

4.中间语言生成:(实现跨平台)

          中间代码是编译器可以被分为前端和后端编译器前端负责产生机器无关的中间代码,编译器后端将中间代码转化为目标机器代码,这样对于一个可以跨平台的编译器而言,他们可以针对不同平台使用同一个前端和针对不同机器平台的数个后端。

5.目标代码的生成和优化:

        源代码级优化器产生中间代码标志着下面的过程都属于编译器后端,编译器后端主要包括代码生成器和目标代码优化器。 代码生成器将中间代码转换成目标机器代码,这个过程十分依赖目标机器,因为不同机器有着不同的字长,寄存器,整数数据类型和浮点数数据类型等。

经过这些扫描语法分析 语义分析 源代码优化 源代码生成和目标代码优化。。。源代码最终被编译为目标代码。但是变量的地址还没有确定,如果变量的定义与对变量的操作源代码在同一个编译单元内,编译器可以为变量分配空间。

三、汇编:   gcc -O test.s -o test.o

对照汇编指令与二进制指令(机器指令)表,将汇编指令转化为二进制指令,几乎一条指令转化为一条二进制指令。结束时生成.o 目标文件

四、链接  (目标文件 库 链接)

库和头文件时两个东西。库是一组目标文件的包,即常用的代码编译成目标文件打包存放。常见的库是运行时库 runtime library     CRT
1.地址和空间的分配:
编译之后的变量、函数的地址并没有确定,链接过程确定。
2.符号决议: 不同模块可能会有相同的变量名或函数名,链接要判断调用的是哪一个
3.重定位: 编译器在编译一个目标文件时,设有假一个变量的确切目标址并不知道,编译器会给他一个0.等到链接时,会将这个地址修正。

在类Unix系统中:
gcc program.c  编译并链接产生一个包含源文件的c程序。
此指令产生一个可执行的程序a.out。。中间会产生一个.o的目标文件,但在链接完成后便删除。
.............
...............
执行:
可执行文件必须加载到内存中,有操作系统的环境由操作系统加载至内存,在独立的环境中,程序的载入由手工加载,或通过可执行代码至入只读内存中。
接着程序开始执行。首先调用主函数main,
开始执行程序代码,此时程序将使用一个运行时堆栈(stack),存储函数的局部变量和地址。出此函数作用域时销毁。程序也可以使用静态内存(static),静态内存中的值保存至整个程序运行结束。
终止程序:分为正常终止main函数和意外终止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值