在ANSI C的实现中,存在2种环境:翻译环境和运行环境。
翻译环境
在翻译环境下,程序的编译可以被分为编译和链接2个部分,其中编译还可以被分为预处理、编译、汇编。下图是程序的编译过程。
- 组成一个程序的每个源文件通过编译器的编译最终都会变成其对应的目标代码
- 每个目标文件由链接器捆绑在一起,最终形成一个单一且完整的可执行程序
- 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库, 将其需要的函数也链接到程序中
编译和链接
接下来再通过一张图去概括编译的4个阶段的具体内容。
预处理
预处理也被称为预编译。在预处理阶段,主要进行以下4个步骤
- #include头文件的包含,顾名思义,就是将源文件中所引用的头文件的所有内容放到源文件之中。
- #define定义的常量的替换,如#define MAX 100,程序中所有出现MAX的位置,都会被替换成100这个整形。
- 条件编译,所有的条件编译指令在这一阶段都会被判断并作用。
- 注释的删除,所有的注释都会被替换成空格。
编译
在这一阶段,主要将C语言代码翻译成汇编代码。除此之外还进行以下4个步骤:
- 词法分析:从左至右逐个字符地对源程序进行扫描,产生单词符号并把字符串形式的源程序改造成为单词符号串形式的中间程序。
- 语法分析:在词法分析识别出正确的单词符号串是否符合语言的语法规则,分析并识别各种语法成分,同时进行语法检查和错误处理,为语义分析和代码生成做准备。
- 语义分析:审查源程序有无语义错误,为代码生成阶段收集类型信息。
- 符号汇总:进行符号的汇总,为在汇编期间形成符号表做准备。
运行环境
运行环境也叫执行环境,也就是程序的执行过程。
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须 由手工安排,也可能是通过可执行代码置入只读内存来完成。
- 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈,存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
- 终止程序。正常终止main函数;也有可能是意外终止。
函数重载
函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数(C语言中不可以),这些函数的函数名必须相同,但是这些函数的形参列表 (参数个数、参数类型、参数顺序)必须有至少1个不同。例如:
void Func(int a, char b, double c)
{}
//参数个数不同
void Func(int a, char b)
{}
//参数类型不同
void Func(char a, char b, char c)
{}
//参数的顺序不同
void Func(int a, double c, char b)
{}
为什么C++支持函数重载而C语言却不支持?
这是由于在链接期间,C++对函数的命名规则可以区分出函数重载的函数,但是C语言的命名规则不可以。
下图是程序在分别采用C语言的编译和C++的编译后的函数名的变化结果。我们可以看出C语言对于函数名没有做什么修改,C++则不同,C++的命名规则是:_Z是每个函数名前面都需要加的,然后Z后面的数字代表这个函数名中有几个字母,随后跟着的是函数名,函数名后面是每个参数的缩写,如:int --> i , char --> c , double --> d , int* --> pi等等。我们可以从C++的命名规则中分析出,在函数名相同的情况下,只要能够让每个函数在经过编译后产生的函数名不同,那么它们就是函数重载。因此影响函数重载的因素如下:
- 函数参数的类型
- 函数参数的个数
- 函数参数的顺序
注意:函数的返回值是函数重载的必要不充分条件。即,函数重载的函数可以返回值不同,但是返回值不同的函数,不一定是函数重载。