The Cherno C++笔记02

news2025/1/18 6:56:13

目录

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.coloradmin.cn/o/1331279.html

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

相关文章

手把手教你在飞书中搭建机器人

前言 大家好&#xff0c;我是潇潇雨声。飞书是一款在国内广受欢迎的企业内部管理和协同工具&#xff0c;同时也可以作为一个强大的个人知识管理工具。在本文中&#xff0c;我将帮助你迅速创建一个飞书对话机器人&#xff0c;并嵌入 chatGPT 的功能。这个机器人可以直接回答你的…

贪吃蛇(七)方向和屏幕刷新

由于用户玩游戏的时候&#xff0c;程序需要一边接收用户的输入&#xff0c;一边需要刷新屏幕&#xff0c;此时就需要引用线程来解决此问题。 实现思路 linux线程库pthread&#xff0c;只需要创建pthread_t 类型的线程变量&#xff0c;然后将线程变量与函数进行绑定即可&#…

HFish蜜罐搭建及简单使用

一、HFish蜜罐 HFish是一款社区型免费蜜罐&#xff0c;侧重企业安全场景&#xff0c;从内网失陷检测、外网威胁感知、威胁情报生产三个场景出发&#xff0c;为用户提供可独立操作且实用的功能&#xff0c;通过安全、敏捷、可靠的中低交互蜜罐增加用户在失陷感知和威胁情报领域的…

一篇文章带你搞定CTFMice基本操作

CTF比赛是在最短时间内拿到最多的flag&#xff0c;mice必须要有人做&#xff0c;或者一支战队必须留出一块时间专门写一些mice&#xff0c;web&#xff0c;pwn最后的一两道基本都会有难度&#xff0c;这时候就看mice的解题速度了&#xff01; 说实话&#xff0c;这是很大一块&…

JavaEE进阶学习:Spring MVC 程序开发

1.什么是 Spring MVC Spring Web MVC 是基于Servlet API 构建的原始 Web 框架&#xff0c;从一开始就包含在Spring 框架中。它的正式名称 “Spring Web MVC” 来自其源模块的名称(Spring-webmvc)&#xff0c;但它通常被称为“Spring MVC”。 从上述定义我们可以得出两个关键信…

blender径向渐变材质-着色编辑器

要点&#xff1a; 1、用纹理坐标中的物体输出连接映射中的矢量输入 2、物体选择一个空坐标&#xff0c;将空坐标延z轴上移一段距离 3、空坐标的大小要缩放到和要添加材质的物体大小保持一致

【雷达原理】雷达测速原理及实现方法

一、雷达测速原理 1.1 多普勒频率 当目标和雷达之间存在相对运动时&#xff0c;若雷达发射信号的工作频率为&#xff0c;则接收信号的频率为&#xff0c;其中为多普勒频率。将这种由于目标相对于辐射源运动而导致回波信号的频率发生变化的现象称为多普勒效应。 如图1-1所示&a…

猫头虎博客:SSH连接失败ssh: connect to host port 22: Connection refused”解决大揭秘

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

Linux bridge开启hairpin模拟测试macvlan vepa模式

看到网上介绍可以通过Linux bridge 开启hairpin方式测试macvlan vepa模式&#xff0c;但是没有找到详细资料。我尝试测试总提示错误信息&#xff0c;无法实现&#xff0c;经过几天的研究&#xff0c;我总算实现模拟测试&#xff0c;记录如下&#xff1a; 参考 1.Linux Macvla…

Angular 11到升级到 Angular 16

日新月异&#xff0c;与时俱进… 随着Angular版本不断更新&#xff0c;再看所开发的项目版本仍然是Angular 11&#xff0c;于是准备升级 截止发博日最版本是 v17.1.0&#xff0c;考虑到稳定性因素决定升级到v16版本 一&#xff1a;查看 升级指南 二&#xff1a;按照指南&…

Ubuntu 常用命令之 cal 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 cal命令在Ubuntu系统下用于显示日历。它可以显示任何特定月份或整个年份的日历。 cal命令的参数如下 -1&#xff1a;只显示当前月份的日历。-3&#xff1a;显示前一个月、当前月和下一个月的日历。-s&#xff1a;指定日历的开始…

力扣思维题——寻找重复数

题目链接&#xff1a;https://leetcode.cn/problems/find-the-duplicate-number/description/?envTypestudy-plan-v2&envIdtop-100-liked 这题的思维难度较大。一种是利用双指针法进行计算环的起点&#xff0c;这种方法在面试里很难说清楚&#xff0c;也很难想到。大致做…

MyBatis中延迟加载,全局和局部的开启使用与关闭

文章目录 MyBatis中延迟加载&#xff0c;全局和局部的开启使用与关闭1、问题提出2、延迟加载和立即加载延迟加载立即加载 3、三种对应的表关系中的加载4、打开全局延迟加载&#xff08;实现一对一的延迟加载&#xff09;5、实现一对多的延迟加载&#xff08;将上面设置的全局延…

Flink 数据序列化

为 Flink 量身定制的序列化框架 大家都知道现在大数据生态非常火&#xff0c;大多数技术组件都是运行在JVM上的&#xff0c;Flink也是运行在JVM上&#xff0c;基于JVM的数据分析引擎都需要将大量的数据存储在内存中&#xff0c;这就不得不面临JVM的一些问题&#xff0c;比如Ja…

thinkphp+vue+mysql酒店客房管理系统 b1g8z

本系统包括前台界面、用户界面和管理员界面、员工界面。在前台界面里游客和用户可以浏览客房信息、公告信息等&#xff0c;用户可以预定客房&#xff0c;在用户中心界面里&#xff0c;用户可以管理预定信息&#xff0c;管理员负责用户预定的审核以及客房的发布、用户的入住等。…

装饰者模式学习

装饰器&#xff08;Decorator&#xff09;模式的定义&#xff1a;指在不改变现有对象结构的情况下&#xff0c;动态地给该对象增加一些职责&#xff08;即增加其额外功能&#xff09;的模式&#xff0c;它属于对象结构型模式。 装饰器模式的主要优点有&#xff1a; 装饰器是继…

基于Java (spring-boot)的在线考试管理系统

一、项目介绍 系统功能说明 1、系统共有管理员、老师、学生三个角色&#xff0c;管理员拥有系统最高权限。 2、老师拥有考试管理、题库管理、成绩管理、学生管理四个模块。 3、学生可以参与考试、查看成绩、试题练习、留言等功能 二、作品包含 三、项目技术 后端语言&#xff1…

【电路笔记】-串联电容器

串联电容器 文章目录 串联电容器1、概述2、示例13、示例34、总结 当电容器以菊花链方式连接在一条线上时&#xff0c;它们就串联在一起。 1、概述 对于串联电容器&#xff0c;流过电容器的充电电流 ( i C i_C iC​ ) 对于所有电容器来说都是相同的&#xff0c;因为它只有一条…

Unity中Shader缩放矩阵

文章目录 前言一、直接相乘缩放1、在属性面板定义一个四维变量&#xff0c;用xyz分别控制在xyz轴上的缩放2、在常量缓存区申明该变量3、在顶点着色器对其进行相乘&#xff0c;来缩放变换4、我们来看看效果 二、使用矩阵乘法代替直接相乘缩放的原理1、我们按如下格式得到缩放矩阵…

Koordinator 支持 K8s 与 YARN 混部,小红书在离线混部实践分享

作者&#xff1a;索增增&#xff08;小红书&#xff09;、宋泽辉&#xff08;小红书&#xff09;、张佐玮&#xff08;阿里云&#xff09; 背景介绍 Koordinator 是一个开源项目&#xff0c;基于阿里巴巴在容器调度领域多年累积的经验孵化诞生&#xff0c;目前已经支持了 K8s…