从源代码到可执行程序的过程
(1)源代码.c文件先经过预处理器,生成一个中间文件.i文件
(2).i文件经过编译生成汇编.s文件
(3).s的汇编文件经过汇编器生成.o的目标文件
(4).o的目标文件经过链接器生成.elf可执行程序
所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。
常用的预处理命令如下:
#define 定义一个预处理宏
#undef 取消宏的定义#if 编译预处理中的条件命令, 相当于C语法中的if语句
#ifdef 判断某个宏是否被定义, 若已定义, 执行随后的语句
#ifndef 与#ifdef相反, 判断某个宏是否未被定义
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足, 则执行#elif之后的语句, 相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足, 则执行#else之后的语句, 相当于C语法中的else
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
defined 与#if, #elif配合使用, 判断某个宏是否被定义#include 包含文件命令
#include_next 与#include相似, 但它有着特殊的用途#line 标志该语句所在的行号
# 将宏参数替代为以参数值为内容的字符窜常量
## 将两个相邻的标记(token)连接为一个单独的标记
#pragma 说明编译器信息#warning 显示编译警告信息
#error 显示编译错误信息
预处理并不分析整个源代码文件, 它只是将源代码分割成一些标记(token), 识别语句中哪些是C语句, 哪些是预处理语句。
预处理器能够识别C标记, 文件名, 空白符, 文件结尾标志。
预处理过程如下:
1.头文件展开ps把#include的文件插入到相应位置
2.宏展开ps展开所有的宏,并删除#define
3.条件编译ps条件预编译指令比如:#if,#ifdef,#else
4.删除注释ps // /**/的内容
5.添加行号和文件名标识
6.保留#pragrma命令ps指示编译器完成特定的动作
编译 : 编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件 , 编译器能够识别代码中的词汇、句子以及各种特定的格式,并将他们转换成计算机能够识别的二进制形式
编译也可以理解为“翻译”,类似于将中文翻译成英文、将英文翻译成象形文字,它是一个复杂的过程,大致包括词法分析、语法分析、语义分析、性能优化、生成可执行文件五个步骤,期间涉及到复杂的算法和硬件架构。
汇编 : 汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种可重定位目标程序的格式,并将结果保存在目标文件hello.o中。hello.o文件是一个二进制文件,它的字节编码是机器语言指令而不是字符,如果我们在文本文件中打开hello.o文件,看到的将是一堆乱码。
链接 : C语言代码经过编译以后,并没有生成最终的可执行文件(.exe 文件),而是生成了一种叫做目标文件(Object File)的中间文件(或者说临时文件)。目标文件也是二进制形式的,它和可执行文件的格式是一样的。对于 Visual C++,目标文件的后缀是
.obj
;对于 GCC,目标文件的后缀是.o
。链接(Link)其实就是一个“打包”的过程,它将所有二进制形式的目标文件和系统组件组合成一个可执行文件。完成链接的过程也需要一个特殊的软件,叫做链接器(Linker)。
在C语言下:
在C++下
Windows下名字修饰规则 :
通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区 分,只要参数不同,修饰出来的名字就不一样,就支持了重载
错误示例
void TestFunc(int a = 10) {
cout<<"void TestFunc(int)"<<endl; }
void TestFunc(int a) {
cout<<"void TestFunc(int)"<<endl; }
//这种类型就不可以进行函数重载
//因为他们的形参的类型是相同的
C++中能否将一个函数按照C的风格来编译?
(1)C++中可以通过在函数声明前加 extern "C" 将一个函数按照 C 语言的风格来进行编译。
(2)C++语言支持函数重载。而C不支持函数重载。
(3)函数在C中和C++中编译过的函数名字是不一样的。加上extern”C”是说明是说明C已经编译过的。