The Cherno C++笔记02

目录

Part 06 How the C++ Compiler Works

1.编译过程

2.C++并不关心文件

3.翻译单元(Translation Unit)

4. 实际用代码感受一下编译过程

4.1 预处理

4.1.1 预处理的本质

4.1.2 预处理后的.i文件

4.1.3 骚操作

4.2 .asm文件(汇编语言源文件)

4.2.1 直观感受优化

4.2.2 常量折叠(Constant Folding)


Part 06 How the C++ Compiler Works

1.编译过程

我们在编译软件上写的内容,实际上就是文本,那从文本到可执行的程序,中间需要经过两步,编译和链接。编译就是将文本转化为.obj后缀的目标文件。

编译又可以分为几个步骤:

①预处理(Preprocessing)

②词法分析(Lexical Analysi)

③语法分析(Syntax Analysis)

④语义分析(Semantic Analysis)

⑤优化(Optimization)

⑥代码生成(Code Generation)

我们可以用一个简单的例子来直观感受一下每个过程

源代码:

#include <stdio.h>int main() {int a = 5;int b = 7;int sum = a + b;printf("Sum is: %d\n", sum);return 0;
}

词法分析生成词法单元(Tokens)

Keyword: include
Identifier: <stdio.h>
Keyword: int
Identifier: main
Punctuation: (
Punctuation: )
Punctuation: {
Keyword: int
Identifier: a
Operator: =
Number: 5
Punctuation: ;
...

语法分析生成抽象语法树(AST)

Program
|
└── Function: main├── Declaration: int a├── Assignment: a = 5├── Declaration: int b├── Assignment: b = 7├── Declaration: int sum├── Assignment: sum = a + b├── Function Call: printf│   ├── String: "Sum is: %d\n"│   ├── Argument: sum└── Return: 0

语义分析:

  • 语义信息:
    • 类型检查通过,变量和函数引用有效。
    • 符号表记录了变量的类型和位置。

优化(Optimization):

int a = 5;
int b = 7;
int sum = a + b;
printf("Sum is: %d\n", sum);
return 0;

代码生成(Code Generation)

MOV a, 5
MOV b, 7
ADD sum, a, b
PRINT "Sum is: %d\n", sum
HALT

转成obj机器码


2.C++并不关心文件

这主要是跟Java做对比

在Java中,一个源代码文件通常对应一个类,且文件名与类名相同,并以 .java 为扩展名。而在C++中:源代码可以分布在多个文件中,每个文件独立地包含了一部分程序的实现。这些文件可以包含函数、类、变量的定义和实现等。默认情况下编译器看到.h文件会将其作为头文件处理,见到.cpp文件会将其作为源文件处理。你可以改变它,不具有强制性。
总而言之:

在Java中,文件名与类名的匹配是强制性的,而且有严格的规范。这种规范有助于提高代码的可读性和可维护性,但也限制了一定的灵活性。

在C++中,虽然有一些约定(例如使用.cpp作为源文件的扩展名),但这些并非强制性规定。C++更加注重灵活性和兼容性,允许开发者使用不同的扩展名或者甚至没有扩展名。这种灵活性允许开发者更自由地组织和命名源文件,但也可能导致一些不规范的实践。


3.翻译单元(Translation Unit)

在编译过程中被处理的最小单元。在C++中一个源文件和一个头文件通常就是一个翻译单元。一个翻译单元作为编译器的输入,经过编译过程后生成一个目标文件,然后多个目标文件可以被链接在一起形成最终的可执行程序。


4. 实际用代码感受一下编译过程

4.1 预处理

4.1.1 预处理的本质

预处理其实就是复制粘贴头文件的内容

我们写两个源文件感受一下:

math.cpp(没有包含任何头文件)

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

Log.cpp(包含iostream头文件)

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

然后我们单独编译(Ctrl+F7)

得到两个obj文件

大小差距很大,造成这种现象的原因就是包含头文件,预处理会把头文件的内容全部复制到Log.cpp中来。

4.1.2 预处理后的.i文件

.i 文件是指预处理后的源文件。在C和C++编译过程中,预处理器会对源文件进行处理,展开宏、处理条件编译指令等,并生成一个经过预处理的中间文件。这个中间文件的扩展名通常是.i

我们可以通过更改下面的设置来让他生成预处理后的.i文件


对Main.cpp文件预处理

#include <iostream>void Log(const char* message);int main()
{Log("Hello World!");//用Log函数实现打印的功能std::cin.get();
}

文本编辑器打开Main.i文件

我们会直观的发现这个现象

4.1.3 骚操作

利用这个特点我们可以实现一些骚操作

建一个只有一个结束大括号的头文件

EndBrace.h

}

然后将原本.cpp文件中的结束大括号改成#include “EndBrace.h”(自己建立的头文件用双引号)还能编译成功吗?

Math.cpp(替换)

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

成功!

打开生成的.i文件

还可以利用宏定义

#define (被替换的内容) (替换后的内容)

#define Age 18 就是在预处理的时候,把代码里面的Age全部替换成18

Math.cpp(替换)

#define INTEGER int
INTEGER Multiply(INTEGER a, INTEGER b)
{return a + b;
}

成功!

打开生成的.i文件

#if 预处理指令用于条件编译。它允许根据指定的条件来选择性地包含或排除部分代码。

Math.cpp(替换)

#if 1
int Multiply(int a, int b)
{return a + b;
}
#endif

打开生成的.i文件


Math.cpp(替换)

#if 0
int Multiply(int a, int b)
{return a + b;
}
#endif

v打开生成的.i文件

4.2 .asm文件(汇编语言源文件)

关闭刚才的设置

更改汇编语言文件输出

这里我们可以看到源代码的汇编语言格式

4.2.1 直观感受优化

Debug版本总共有253行

如果我们改一下Debug版本的设置为

这个时候我们继续运行编译

只有177行

4.2.2 常量折叠(Constant Folding)

常量折叠(Constant Folding)是编译器在编译时对表达式中的常量进行计算和简化的过程。在这个过程中,编译器会尽可能地将表达式中的常量计算出结果,以减少运行时的开销。

改一下Math.h

int Multiply()
{return 2*5;
}

编译,并查看生成的汇编文件.asm

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/240370.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安装nodejs,配置环境变量并将npm设置淘宝镜像源

安装nodejs并将npm设置淘宝镜像源 1. 下载nodejs 个人不喜欢安装包&#xff0c;所以是下载zip包的方式。这里我下载的node 14解压包版本 下载地址如下&#xff1a;https://nodejs.org/dist/v14.15.1/node-v14.15.1-win-x64.zip 想要其他版本的小伙伴去https://nodejs.org/di…

黑豹程序员-读properties属性文件本地正常,打包jar后运行出错

读properties属性文件本地正常&#xff0c;打包jar后运行出错 java.io.FileNotFoundException:file:\D:\code\xml-load\target\XX.jar!\XXX(文件名、目录名或卷标语法不正确。)原因是读取方式不正确 当使用Spring Boot将应用打成jar时&#xff0c;需要读取resources目录下配置…

C++ 检测 是不是 com组件 的办法 已解决

在日常开发中&#xff0c;遇到动态库和 com组件库的调用 无法区分。检测是否com组件的办法 在头部文件&#xff0c;引入文件 如果能编译成功说明是 com组件&#xff0c;至于动态库如何引入&#xff0c;还在观察中 最简单办法 regsvr32 TerraExplorerX.dll 是com 组件 regs…

LLaMA开源大模型源码分析!

Datawhale干货 作者&#xff1a;宋志学&#xff0c;Datawhale成员 花了一晚上照着transformers仓库的LLaMA源码&#xff0c;把张量并行和梯度保存的代码删掉&#xff0c;只留下模型基础结构&#xff0c;梳理了一遍LLaMA的模型结构。 今年四月份的时候&#xff0c;我第一次接触…

OpenAI 疑似正在进行 GPT-4.5 灰度测试!

‍ 大家好&#xff0c;我是二狗。 今天&#xff0c;有网友爆料OpenAI疑似正在进行GPT-4.5灰度测试&#xff01; 当网友询问ChatGPT API调用查询模型的确切名称是什么时&#xff1f; ChatGPT的回答竟然是 gpt-4.5-turbo。 也有网友测试之后发现仍然是GPT-4模型。 这是有网友指…

自动化测试架构设计必会知识点——对核心业务进行封装复用(附Java源码)

随着UI自动化测试工具可选性越来越多&#xff0c;工具也越来越稳定&#xff0c;前几年关于自动化测试架构设计的概念逐渐淡化&#xff0c;但是做自动化测试最重要的两点—— PO设计模式和核心业务的封装复用大家还是必须掌握的&#xff0c;前面的文章我已经介绍了什么是PO设计模…

交叉熵损失(Cross Entropy Loss)学习笔记

在分类任务中&#xff0c;我们通常使用交叉熵作为损失函数&#xff0c;首先给出交叉熵的计算公式&#xff1a; 二分类中&#xff1a; L 1 N ∑ i L i 1 N ∑ i − [ y i l o g ( p i ) ( 1 − y i ) ⋅ l o g ( 1 − p i ) ] \mathcal{L}\frac1{N}\sum_{i}L_i\frac1{N}\sum…

基于 Sentry 的前端监控系统搭建(Linux)

一、前言 随着技术这几年的发展与沉淀&#xff0c;线上数据指标监控也变得尤为重要&#xff0c;研发人员和运营人员需要对线上的产品指标有所感知&#xff0c;同时风险也需要及时暴露&#xff0c;很多公司开始自建监控系统&#xff0c;但对于一些定制化要求不是特别高的团队&a…

【PHP】Yii2 使用validate规则验证

目录 提交验证 声明规则 特殊验证 一个特殊验证的示例 内联验证器 一个完整示例 参考文档 提交验证 根据经验&#xff0c;您永远不应该相信从最终用户那里收到的数据&#xff0c;并且应该在很好地使用这些数据之前对其进行验证。 给定一个model模型&#xff0c;用户输入填…

网络爬虫之Ajax动态数据采集

动态数据采集 规则 有时候我们在用 requests 抓取页面的时候&#xff0c;得到的结果可能和在浏览器中看到的不一样&#xff0c;在浏览器中可以看到正常显示的页面教据&#xff0c;但是使用 requests 得到的结果并没有&#xff0c;这是因为requests 获取的都是原始的 HTML 文档…

前端跨页面交互信息或传递信息都有这么几种方式,总有一个满足你的使用场景

同源页面通信&#xff1a; 方法&#xff1a;Broadcast Channel 【广播】 这个可以查看我另一篇文章有使用案例 同一来源的不同文档&#xff08;在不同的窗口、选项卡、框架或 iframe 中&#xff09;之间进行通信 // page1<!DOCTYPE html> <html lang"en"…

(1)(1.9) MSP (version 4.2)

文章目录 前言 1 协议概述 2 配置 3 参数说明 前言 ArduPilot 支持 MSP 协议&#xff0c;可通过任何串行端口进行遥测、OSD 和传感器。这样&#xff0c;ArduPilot 就能将遥测数据发送到 MSP 兼容设备&#xff08;如大疆护目镜&#xff09;&#xff0c;用于屏幕显示&#x…

(C)一些题15

1.下列关于 C 语言程序结构的说法中&#xff0c;不正确的是&#xff08;D) A &#xff0e;一个程序由一个或多个源程序文件组成 B &#xff0e;函数是 C 程序的主要组成部分 C &#xff0e;程序总是从 main 函数开始执行的 D . C 语言本身提供了许多输入输出语句 解析&…

【Java基础】01- 基础概念

注释和关键字 注释 定义&#xff1a;代码中需要一些解释说明性的文字&#xff0c;通常称为注释。 单行注释&#xff1a; // 注释信息多行注释&#xff1a;/* 注释信息 */文档注释&#xff1a;/** 注释信息 */ 文档注释可以利用Java中自带的DOC工具&#xff0c;自动生成相关代…

银河麒麟v10 安装mysql 8.35

银河麒麟v10 安装mysql 8.35 1、卸载mariadb2、下载Mysql安装包3、安装Mysql 8.353.1、安装依赖包3.2、安装Mysql3.3、安装后配置 1、卸载mariadb 由于银河麒麟v10系统默认安装了mariadb 会与Mysql相冲突&#xff0c;因此首先需要卸载系统自带的mariadb 查看系统上默认安装的M…

MyBatis动态SQL中if,where,set,trim四种标签的使用和联系

目录 MyBatis动态SQL中if&#xff0c;where&#xff0c;set&#xff0c;trim四种标签的使用和联系1、先介绍trim标签以下是trim标签中涉及到的属性&#xff1a; 2、使用trim标签或where标签去除多余的and关键字3、使用trim标签或set标签去除多余的逗号 MyBatis动态SQL中if&…

前端常用的开发工具

前端常用的开发工具&#x1f516; 文章目录 前端常用的开发工具&#x1f516;1. Snipaste--截图工具2. ScreenToGif--gif图片录制3. Typora--Markdown编辑器4. notepad--文本代码编辑器5. uTools--多功能工具6. EV录屏--录屏软件7. Xmind--思维导图8. Apifox -- 接口调试9. Tor…

【大数据】NiFi 中的 Controller Service

NiFi 中的 Controller Service 1.Service 简介1.1 Controller Service 的配置1.1.1 SETTING 基础属性1.1.2 PROPERTIES 使用属性1.1.3 COMMENT 页签 1.2 Service 的使用范围 2.全局参数配置3.DBCPConnectionPool 的使用样例4.在 ExcuseGroovyScript 组件中使用 Service 1.Servi…

记一次 Nginx 调参的踩坑经历

最近在基于SSE&#xff08;Server Sent Events&#xff09;做服务端单向推送服务&#xff0c;本地开发时一切顺利&#xff0c;但是在部署到预发环境时就碰到1个很诡异的问题&#xff0c;这里需要简单介绍下我们的整体架构&#xff1a; 整体架构 可以看到所有的请求都会先到统一…

2024 年 22 款顶级免费数据恢复软件比较 [Windows 和 Mac]

适用于 Windows 和 Mac 用户的最佳数据恢复软件下载列表和比较&#xff0c;可快速恢复丢失的数据、已删除的文件、照片或格式化的分区数据&#xff1a; 数据恢复软件是一种从任何存储介质恢复丢失文件的应用程序。它可以恢复由于病毒攻击、硬盘故障或任何其他原因而意外删除或…