在vs2019的环境下认识C++(3)

这一篇我们重点讲讲编译(compiling)的相关知识
首先我们要知道在C++中没有文件的概念,这一点或许跟JAVA有所不同,我们在编译器中的文件只是为了更好的识别源码而设立的。.cpp文件对应C++,.h文件对应头文件,这是编译器中已经设置好的。我甚至可以用让编译器对任何种类的文件按C++编译,这是没问题的,文件不代表任何东西。
回到我们之前所写的函数
在这里插入图片描述
如果我们再次观察这两个.obj文件我们可能会惊讶于他们的大小,我们只写了几行代码然而却又几十kb的大小。
这是因为文件中存在#include<iostream>这句话。
在编译的第一阶段,首先会进行的是预编译,常见的形式有include,define,if,ifdef,这里我们先将include。
在预编译时遇到#include<>,编译器会打开该文件,然后复制粘贴所有的代码到本文件
我们再新建一个.cpp文件命名为math,写入

int Multiply(int a, int b)
{
	int resualt = a * b;
	return resualt;
}

编译之后我们再次打开文件夹,可以看到:
在这里插入图片描述
同样都是几行代码,math.obj只有3kb。
我们再新建一个头文件,命名为EndBrace.h
在这里插入图片描述
删除头文件中所有的代码然后加上一个右大括号
在这里插入图片描述
我们再来修改math.cpp代码

int Multiply(int a, int b)
{
	int resualt = a * b;
	return resualt;
	#include"EndBrace.h"

我们删除了函数的右大括号并且加上了#include"EndBrace.h",再对该文件进行编译,我们发现即使没有右大括号我们仍然能够编译成功
在这里插入图片描述
因为#include就是将文件内容完完整整的复制粘贴过来,这就是头文件的工作原理。
为了更加深入的理解头文件,我们要先对VS做一些调整
在这里插入图片描述
在“Project1”的属性中,我们把“预处理的文件”这一项由否改为是,然后再对math.cpp进行编译,打开文件夹,可以看到新生成的文件
在这里插入图片描述我们用记事本打开,就可以看到其中的内容
在这里插入图片描述你看,它真的就是把右大括号复制了进来。
接下来我们再次修改math.cpp的内容

#define INTERGER int
INTERGER Multiply(int a, int b)
{
	INTERGER resualt = a * b;
	return resualt;
}

编译之后可以再次打开之前那个math.i文件
在这里插入图片描述
复制粘贴,就是这样。
再说到#if语句,这个语句可以帮助我们选择和剔除部分代码
比如我们将代码改为

#if 1
int Multiply(int a, int b)
{
	int resualt = a * b;
	return resualt;
}
#endif 

1代表true,因此我们打开文件可以看到‘在这里插入图片描述
而如果我们将1改为0,代码就会被禁用
在这里插入图片描述
可以看到编译之后代码全部消失了。
这样之后我们就可以查看iostream的内容了,这是一个非常非常大的文件,其中又俄罗斯套娃般的#include着其他文档
恐怖的大小
整个代码超过了3w行
当我们预编译结束后,我们就可以把C++代码转变为机器码了,我们关掉之前对VS作出的调整,再次编译生成.obj文件,会发现这是一个二进制文件,但如果我们想看看其中的内容我们应该怎么做呢
在这里插入图片描述
VS仍然为我们提供了方法,像上图一样调整设置即可。再次编译可以得到.asm文件,仍然是用记事本打开
在这里插入图片描述
这其实是个汇编语言,红色方框内是cpu执行乘法的语句,只需稍稍了解汇编应该就能懂得这四句话的作用。
这个地方存在一个可优化的点就是,我们把eax(寄存器)的值传给resualt,又把resualt传回eax,这是一种没有意义浪费时间的做法
我们可以将程序改为

int Multiply(int a, int b)
{
	return a*b;
}

这里我们直接返回a*b而不依赖于中间变量。
在这里插入图片描述
这样我们就仅仅是在eax内进行了乘法运算,提高了效率。
如果我们再看看这个.asm文件可能会觉得奇怪,为什么会有这么多奇怪的东西存在,因为我们是在DEBUG环境下编译的,编译器不仅不会做优化,甚至会加入一些额外的东西以使得我们的程序便于DEGUG,当然我们也可以关掉它
在这里插入图片描述
我们再次编译之后可以看到.asm文件少了很多东西,其实我们是去除了编译器优化的那一部分
我们再次对程序做一些更改

int Multiply()
{
	return 5*2;
}

编译之后查看.asm文件
在这里插入图片描述可以看到编译的过程中会直接把5乘以2计算出来,这就是所谓的常量折叠(costant folding),有些时候甚至会带来错误,所以要尽量避免这种情况。
我们再写两个函数

const char* Log(const char* message)
{
	return message;
}
int Multiply(int a,int b)
{
	Log("Multiply");
	return 5*2;
}

关闭优化之后进行编译:我们可以看到log函数转移了字符串到eax寄存器
在这里插入图片描述
而在Multiply函数中可以看到对Log的调用(call)
在这里插入图片描述你可能会想为什么这些函数都夹杂着一些奇怪的字符,这就是函数签名。这其实又跟链接有关,在链接时,要准确的查找你所定义的函数,就需要独一无二的函数签名。
我们可以打开优化再次进行编译,我们发现编译器直接优化掉了对Log的调用,因为它认为这没有意义。
在这里插入图片描述

发布了7 篇原创文章 · 获赞 0 · 访问量 109
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览