IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++学习:编译 -> 正文阅读

[C++知识库]C++学习:编译

编译

为了编译 C++ 程序,我们使用 C++ 编译器。C++ 编译器按顺序遍历程序中的每个源代码 (.cpp) 文件。

它会检查您的代码以确保它遵循 C++ 语言的规则。如果没有,编译器会给你一个错误(和相应的行号)以帮助确定需要修复的内容。编译过程也将中止,直到错误被修复。

将源代码转变为可执行的二进制主要需要两个步骤,第一是编译,第二是链接。编译主要是将源代码转变为中间形式,obj,目标文件,然后目标文件经由链接器连接,生成了可执行代码。

对于编译过程而言,首先要做的事情是预处理,预处理对所有的预处理项进行相应处理,然后进行分词和语法处理,将人比较容易看懂的代码生成编译器比较容易处理的语法树,然后将所有的代码要么生成常量,要么生成指令。一旦生成了语法树,就可以进行代码生成了。

对于C++语言,每个.cpp文件就是一个翻译单元,每个.cpp文件都会被翻译成一个.obj文件。

Main.cpp

#include <iostream>

void Log(const char* message);

int main()
{
	Log("hello,wrold");
	
	std::cin.get();

	return 0;
}

Log.cpp

#include <iostream>
void Log(const char* message)
{
	std::cout << message << '\n';
}

例子中的两个源代码log.cpp和main.cpp大小都不大,但是生成的.obj文件体积较大.
在这里插入图片描述

在这里插入图片描述

这个原因主要是#include 所导致的。如何理解#include这个预处理,创建一个新的小的cpp文件,math.cpp

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

这个cpp文件没有任何的#include或者其他的预处理项。来看下编译的第一步,预处理。常见的预处理项包括#define,#include,#if, #endif等。先看一下最常见的#include。#include非常简单,它指定了你想要包含的文件,预处理器打开那个文件,阅读里面内容然后要包含的内容贴贴到文件中。看一个例子,创建一个新的头文件,EndBrace.h,在这个文件中就只有一个右大括号

在这里插入图片描述

按ctrl+F7进行编译报错

在这里插入图片描述

左边的大括号没有被匹配。因为我们确实将右括号给删了。

接下来,用#include预处理来处理
在这里插入图片描述
在这里插入图片描述

现在编译的话,完全没问题。原因就是预处理的时候,编译器就是取打开了EndBrace.h,然后将它里边的内容拷贝到了math.cpp,这样的话,大括号就匹配完成了。

在这里插入图片描述

接下来看的更细致一点,修改一下VS的配置,让它输出编译的过程中产生的预处理的结果。右键点击项目,选择属性,将预处理到文件的“否“,改成“是“。

ctrl+f7重新编译,此时可以在debug目录下,发现math.i。打开math.i,可以发现内容如下:
在这里插入图片描述

通过上面的操作,应该能够理解#include的作用。

最后看一下#include 的效果。可以看到Math.i多达五万多行,而这么多代码,主要就是iostream的内容。

在这里插入图片描述

接下来,恢复配置,不再输出预处理代码,预处理代码都删去;剩下的cpp代码。

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

观察一下目标文件。如果直接使用VS打开math.obj,那么可以看到都是二进制。这时候可以修改一下配置,让生成obj的同时输出asm,好看一点。可以通过项目 -> 属性 -> c/c++ -> 输出文件

在这里插入图片描述

此时,可以生成.asm文件,打开ams文件

在这里插入图片描述

先不要太在意这些汇编代码细节,这个例子讲编译器优化。在上面的Line2的代码中,可以看到首先将变量a保存在寄存器eax中,接下来看到 imul 指令,这里就是将变量a和变量b进行相乘,并且将结果保存在eax中。

然后将eax中的值放在变量result中,最后将result的中又放回了eax。因为eax存放的是函数的返回值。

上面看上去重复的动作主要是因为math.cpp中声明并返回了result变量。这就是如果我们不要求编译器进行优化,那么,编译器产生的代码会很慢。

可以修改一下math.cpp,看一下生成的asm,进行一下对比:


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

生成的汇编代码会得到精简但是不明显。

再看一个例子,如果如下修改math.cpp:

int Multiply() {
	
	return 2 * 5;

}

此时可以看到,直接将10移入了eax。这里主要是想说明 常量折叠 (constant folding)的概念。也即,如果代码中有常量,那么在编译的时候就已经计算出了这个常量,并不会将计算遗留到运行时。

在这里插入图片描述

下面看一个函数调用的例子

const char* Log(const char* message) {
	return message;
}


int Multiply(int a, int b) {
	
	Log("multiply");
	return a * b;

}

这里的Log并没有什么用,只是用来演示一下函数调用。当查看汇编码时,可以看到在对应于multiply函数的汇编码中,有call指令,以及之下有imul指令

在这里插入图片描述

Log函数名字之后跟着的乱码和@@ 称为函数的签名,便于链接器定位到函数。这里的代码其实有冗余,Log函数基本没有任何作用。我们可以试一下优化

在这里插入图片描述

如果遇到和RTC1冲突,可以修改

在这里插入图片描述

这样的话, 可以看到生成的汇编码将 call Log完全地给优化掉了

在这里插入图片描述

通过以上的例子,对编译器的工作应该有了基本的了解

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-17 12:34:54  更:2021-11-17 12:35:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:03:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码