简单来说,由C++代码文件生成可执行文件的过程如下:
C++代码文件我们都不陌生,但实际上它的后缀名 .cpp 只是想对编译器表明,自己需要被当作C++文件来编译,所以即使你改为 .txt 后缀用记事本打开它一样是可以的。
对于每一个 .cpp 文件,编译器都会为它生成一个 .obj 文件,如下所示:
在VS里面对每一个 cpp 文件进行编译(CTRl + F7),可以得到三个 obj 文件:
而在编译之前,首先执行的是预处理语句,如#include
#define
#if #endif
#include
的作用实际上就是找到对应文件,然后复制粘贴到当前文件而已,如下例:
//Math.cpp
int Multiply(int a, int b)
{int result = a * b;return result;
}
我们可以创建一个头文件Endbrace.h如下,里面只有一个右大括号:
//Endbrace.h
}
然后把Math.cpp改成这样:
//Math.cpp
int Multiply(int a, int b)
{int result = a * b;return result;
#include "EndBrace.h"
完全没有问题!这说明#include
的功能就是复制粘贴而已。
我们可以通过如下设置看到预处理后的文件是怎么样的(项目右键-属性):
此时编译就不会生成 obj 文件了,而是生成了一个 .i 文件,用记事本打开如下:
如果我们看下#include <iostream>
后的结果就知道,里面会包含五万多行的代码,所以这就是为什么加上这个语句后编译出来的obj文件会变得这么大。
#define
则是把某个东西替换为另一个东西,如下:
//Math.cpp
#define ZhengShu intZhengShu Multiply(int a, int b)
{ZhengShu result = a * b;return result;
}
可以看到预处理后的文件内容与原 Math.cpp 是一样的。
#if #endif
则是根据条件决定后面的内容是否存在,例如:
//Math.cpp
#if 1int Multiply(int a, int b)
{int result = a * b;return result;
}#endif
效果不变,而
//Math.cpp
#if 0int Multiply(int a, int b)
{int result = a * b;return result;
}#endif
文件就变空了,因为#if
后为0,不满足条件。
最后讲一下这个 obj 文件,直接打开的话是二进制,我们是看不懂的,但是我们可以把它变成汇编语言,设置如下:
现在进行编译,我们会看到一个 .asm 文件,这就是汇编文件了:
用记事本打开如下(78行),imul 就是乘法操作:
红色框中的代码执行的任务是,将变量 a 移动到寄存器 eax,然后将变量 b 与 eax 相乘且乘积放在 eax(a * b
),之后就把 eax 的值移动到变量 result(result = a * b
),最后是把变量 result 又移动到寄存器 eax 中(return result
)。实际上最后两行代码是多余的,可以对其进行优化。
再再最后,关于 VS 中的 Debug 和 Release,后者是经过优化的所以速度更快,但是不方便调试。如果我们想要在 Debug 中进行优化也是可以的,设置如下:
直接编译会报错,还需要以下设置:
优化之后编译出来的文件(47行),很明显就精简不少了: