程序的执行过程(C语言)

程序的编译和预处理操作(C语言)

总述

在ANSI C的任何一种实现中,存在两个不同的环境。一种是翻译环境,第二种是执行环境。我们代码实现需要经过两种大的步骤——编译和运行。以C语言程序为例,我们写完代码后,需要经过编译和运行才能完成整个过程,而编译又包含4个步骤,分别为:预编译,编译,汇编,链接。当连接完成后,就可以执行运行操作。

预编译

预处理操作简单的解释就是我们在进行编译(总过程)中进行的准备过程,举个例子,我们在床上准备吃午饭,我们需要做好穿衣服的准备工作才可以去吃饭,那么具体的准备工作有哪些呢?

#define

举个例子:

#define MAX 1000
#define reg register          //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;)     //用更形象的符号来替换一种实现
#define CASE break;case        //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
                          date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ ,       \
__DATE__,__TIME__ )

#define是最为常见的预处理符号,我们在初始C语言中曾简单介绍过#define的简单用法,MAX此时是常量,为1000,预编译会将代码中出现的MAX全部替换为1000,而reg则是register的替换,同样预处理会将reg全部替换成register。

预定义符号解释
_FILE_进行编译的源文件
_LINE_文件当前的行号
_DATE_文件被编译的日期
_TIME_文件被编译的时间
_STDC_如果编译器遵循ANSI C,其值为1,否则未定义

关于#define还有一个注意点,就是宏的概念,举个例子:

#define MAX(a, b) ((a)>(b)?(a):(b)) 

这个宏是用于比较两个数的大小的,那么为什么不用函数来完成这个任务呢?原因有二:

  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
  2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。
    比较函数和宏:
属性#define宏定义函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码
执行速度更快存在函数的调用和返回的额外开销,所以相对慢一些
操作符优先级宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号。函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。
带有副作用的参数参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。函数参数只在传参的时候求值一次,结果更容易控制。
参数类型宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型。函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是不同的。
调试宏是不方便调试的函数是可以逐语句调试的
递归宏是不能递归的函数是可以递归的

头文件

预处理也会处理头文件,关于头文件,我们会有这样一个问题,就是头文件有时会用<>,有时也会使用"",举个例子:

#include <stdio.h>
#include "stdio.h"

当我们用#include "stdio.h"时,预处理会在工程文件夹寻找自己编写的头文件,如果没有发现再去头文件中寻找相对应的库函数,而#include <stdio.h>不一样,<>的引用预处理就只会在库函数中寻找,如果找不到就会报错。

编译

编译过程就会进行语法分析,词法分析,语义分析,符号分析,我们通常报错等的问题就会出现在这个步骤。

语法分析

编译程序的语法分析器以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,如表达式、赋值、循环等,最后看是否构成一个符合要求的程序,按该语言使用的语法规则分析检查每条语句是否有正确的逻辑结构,程序是最终的一个语法单位。编译程序的语法规则可用上下文无关文法来刻画。

词法分析

词法分析的任务是对由字符组成的单词进行处理,从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号串的中间程序。执行词法分析的程序称为词法分析程序或扫描器。
源程序中的单词符号经扫描器分析,一般产生二元式:单词种别;单词自身的值。单词种别通常用整数编码,如果一个种别只含一个单词符号,那么对这个单词符号,种别编码就完全代表它自身的值了。若一个种别含有许多个单词符号,那么,对于它的每个单词符号,除了给出种别编码以外,还应给出自身的值。

汇编

汇编过程会形成符号表,将汇编指令转化为二进制指令
汇编指令是在编译中形成,汇编指令是汇编语言中使用的一些操作符和助记符,还包括一些伪指令(如assume,end),汇编指令同机器指令一一对应。每一种CPU都有自己的汇编指令集。我们知道机器只能识别二进制指令,所以汇编过程就是将汇编指令转化为二进制指令。

链接

链接过程就是将之前形成的符号表进行合并,形成一个完成的可执行程序。
在这里插入图片描述
整个程序的执行过程就是这样的,当然还有一些细节方面的问题,希望能给大家带来帮助,谢谢各位。

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Solitudefire

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值