高级编程语言的基本语法在CPU的眼中是什么样的呢?

news2025/1/18 7:32:35

任何一门高级编程语言,就一定存在下面这几个语法元素

  1. 变量
  2. 类型
  3. 数组
  4. 控制语句(条件,循环)
  5. 运算符(算术运算,布尔运算,赋值运算,关系运算,位运算)
  6. 函数

而本节探究的是,这6个语法元素在CPU的眼中是什么样子的呢?我们先来看看变量。

说到变量我们的先从内存说起,为了方便管理,整个内存被划分为一块一块的,我们把这样一块的内存叫做内存单元,通常情况下,一块内存单元的大小为一个字节,我们需要给这些内存单元编号,从0开始,而这个编号有个专门的名字,叫做内存地址。CPU比较偏爱内存地址,因为知道内存地址就可以操作对应的内存单元。但是我们并不喜欢内存地址,因为内存地址是一串数字,没有任何可读性,于是我们映入变量的概念,变量就是这块内存单元的别名。一个比较合适的类比:变量与内存地址的关系和域名与IP地址的关系一样。比如下面这两段代码

#include <stdio.h>
int main() {
    int a = 1;
    return 0;
}
main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 1 ; 这里就是 int a = 1;
        mov     eax, 0
        pop     rbp
        ret

接下来我们来谈谈类型,类似其实有两个作用,对于我们开发者而言,必要的类型检验可以帮我我们减少代码错误。对于CPU而言,类型指定了操作数的大小。比如下面这两段代码:

#include <stdio.h>
int main() {
    int num1 = 1;
    long num2 = 100;
    return 0;
}
main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 1 ; int num1 = 1;
        mov     QWORD PTR [rbp-16], 100 ; long num2 = 100;
        mov     eax, 0
        pop     rbp
        ret

DWORD 表示操作4个内存单元,QWORD 表示操作8个内存单元

基本上每个编程语言都提供了数组这个基础的数据结构,为什么呢?因为现实世界需要,因为有这样的需求。通常意义上,数组是存储多个同类型的数据结构,这意味这他的内存结构是连续的。所以对于CPU而言,他不过是一块连续的内存单元而已。

控制语句可以说是编程语言的灵魂,全部的程序都是由条件语句,循环语句这样像搭积木一样搭建出来的。而这些控制语句在CPU的眼中,不过是几条固定的指令。

#include <stdio.h>

int main() {
    int a = 10;
    int b = 9;
    if (a > b) {
        printf("a more than b");
    }else {
        printf("b more than a");
    }
}
.LC0:
        .string "a more than b"
.LC1:
        .string "b more than a"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 10
        mov     DWORD PTR [rbp-8], 9
        mov     eax, DWORD PTR [rbp-4]
        cmp     eax, DWORD PTR [rbp-8]
        jle     .L2
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        jmp     .L3
.L2:
        mov     edi, OFFSET FLAT:.LC1
        mov     eax, 0
        call    printf
.L3:
        mov     eax, 0
        leave
        ret

可以发现控制语句对应的指令就是 jxx 循环语句也是一样的,只不过不是跳转的位置不是往后,而是往前。

#include <stdio.h>

int main() {
    int sum = 0;
    for (int i = 0; i<= 100; i++) {
        sum += i;
    }
}
main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 0
        mov     DWORD PTR [rbp-8], 0
        jmp     .L2
.L3:
        mov     eax, DWORD PTR [rbp-8]
        add     DWORD PTR [rbp-4], eax ; sum += i;
        add     DWORD PTR [rbp-8], 1 ; i++
.L2:
        cmp     DWORD PTR [rbp-8], 100 ; i <= 100;
        jle     .L3
        mov     eax, 0
        pop     rbp
        ret

高级语言中的运算符就更加不用说了,不过是一些运算指令。到此编写一个程序所需要的全部语法在CPU层面都已经解构完毕了,而函数不过是一种让程序模块化的最基本的手段。方便我们在编写庞大,复杂的程序时,能够更加简单,更加灵活。那么函数在CPU的眼中是什么样子的呢?

函数的出现,让变量的生命周期(也叫作用域)有了区别,函数内部的变量会随着函数的调用而创建,函数的返回而销毁。这样做的目的是充分利用内存。接下来我们通过一个例子来看看函数是如何被调用的,又是如何被返回的。

#include <stdio.h>

int f1(int num) {
    int max = 100;
    return num + max;
}

int main() {
    int init = 10;
    int res = f1(init);
    return 0;
}
f1:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-20], edi
        mov     DWORD PTR [rbp-4], 100
        mov     edx, DWORD PTR [rbp-20]
        mov     eax, DWORD PTR [rbp-4]
        add     eax, edx
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 10
        mov     eax, DWORD PTR [rbp-4]
        mov     edi, eax
        call    f1
        mov     DWORD PTR [rbp-8], eax
        mov     eax, 0
        leave
        ret

可以发现,调用函数会使用call指令,这个指令的作用是将下一条的指令入栈,然后跳转到f1代码段,在每个函数的开头都有这两行指令,push rbp mov rbp, rsp 这两条指令的作用是,先保存上一个函数的栈帧起点,然后重置栈帧的起点为当前的栈顶,即创建一个新的栈帧。然后再存放局部变量。然后函数结束,pop rbp ret 执行这两条指令,rbp寄存器回到上一个函数的栈帧的起点,ret指令让指令寄存器IP,回到调用函数的位置继续执行。

到此,相信你一定有所体会,CPU很呆板,只会按照我们写好的指令一条一条的执行。我们可以看到比较高级的语法,例如函数调用其实不是CPU本身就支持,而是我们通过一些额外的指令让CPU可以做到函数调用,而这些额外的指令都是编译器生成的。所以我们常常说一个语言是否支持某种语言特性,取决于它的编译器

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

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

相关文章

业务架构、数据架构、应用架构和技术架构

TOGAF(The Open Group Architecture Framework)是一个广泛应用的企业架构框架&#xff0c;旨在帮助组织高效地进行架构设计和管理。 TOGAF 的核心就是由我们熟知的四大架构领域组成:业务架构、数据架构、应用架构和技术架构。 企业数字化架构设计中的最常见要素是4A 架构。 4…

python爬虫入门(实践)

python爬虫入门&#xff08;实践&#xff09; 一、对目标网站进行分析 二、博客爬取 获取博客所有h2标题的路由 确定目标&#xff0c;查看源码 代码实现 """ 获取博客所有h2标题的路由 """url "http://www.crazyant.net"import re…

简历_使用优化的Redis自增ID策略生成分布式环境下全局唯一ID,用于用户上传数据的命名以及多种ID的生成

系列博客目录 文章目录 系列博客目录WhyRedis自增ID策略 Why 我们需要设置全局唯一ID。原因&#xff1a;当用户抢购时&#xff0c;就会生成订单并保存到tb_voucher_order这张表中&#xff0c;而订单表如果使用数据库自增ID就存在一些问题。 问题&#xff1a;id的规律性太明显、…

win32汇编环境,窗口程序中对多行编辑框的操作

;运行效果 ;win32汇编环境,窗口程序中对多行编辑框的操作 ;比如生成多行编辑框&#xff0c;显示文本、获取文本、设置滚动条、捕获超出文本长度消息等。 ;直接抄进RadAsm可编译运行。重点部分加备注。 ;下面为asm文件 ;>>>>>>>>>>>>>&g…

【Flink系列】5. DataStream API

5. DataStream API DataStream API是Flink的核心层API。一个Flink程序&#xff0c;其实就是对DataStream的各种转换。具体来说&#xff0c;代码基本上都由以下几部分构成&#xff1a; 5.1 执行环境&#xff08;Execution Environment&#xff09; Flink程序可以在各种上下文…

探索未来:Leap Motion JavaScript框架——开启VR与手势控制的无限可能

探索未来&#xff1a;Leap Motion JavaScript框架——开启VR与手势控制的无限可能 leapjs JavaScript client for the Leap Motion Controller 项目地址: https://gitcode.com/gh_mirrors/le/leapjs 项目介绍 欢迎来到Leap Motion JavaScript框架的世界&#xff01;Lea…

PCM5142集成32位384kHz PCM音频立体声114dB差分输出DAC编解码芯片

目录 PCM5142 简介PCM5142功能框图PCM5142特性 参考原理图 PCM5142 简介 PCM514x 属于单片 CMOS 集成电路系列&#xff0c;由立体声数模转换器 (DAC) 和采用薄型小外形尺寸 (TSSOP) 封装的附加支持电路组成。PCM514x 使用 TI 最新一代高级分段 DAC 架构产品&#xff0c;可实现…

技术领衔 互学互鉴|ZASM召开2024年度技术交流会

1月16日&#xff0c;ZASM组织召开了“2024年度企业员工技术交流活动”。公司总经理&#xff0c;技术部门负责人及项目经理参加本次会议。 会上&#xff0c;公司所属各项目技术负责人围绕“三维模型切割模块的基础操作与模型发布缓存的技术演示”、“J18微型智能空中作业平台的…

UI自动化测试:异常截图和page_source

自动化测试过程中&#xff0c;是否遇到过脚本执行中途出错却不知道原因的情况&#xff1f;测试人员面临的不仅是问题的复现&#xff0c;还有对错误的快速定位和分析。而异常截图与页面源码&#xff08;Page Source&#xff09;的结合&#xff0c;正是解决这一难题的利器。 在实…

OSI七层协议——分层网络协议

OSI七层协议&#xff0c;顾名思义&#xff0c;分为七层&#xff0c;实际上七层是不存在的&#xff0c;是人为的进行划分,让人更好的理解 七层协议包括&#xff0c;物理层(我),数据链路层(据),网络层(网),传输层(传输),会话层(会),表示层(表),应用层(用)(记忆口诀->我会用表…

浅谈计算机网络04 | 现代网络需求与技术支撑

现代网络需求与技术支撑 一、网络和因特网流量的类型剖析1.1 弹性流量的自适应特征1.2 非弹性流量的刚性特征1.3 实时流量特性 二、特定领域的网络需求解析2.1 大数据环境下的网络需求分析2.2 云计算环境下的网络需求分析2.3 移动数据环境下的网络需求分析 三、QoS和QoE&#x…

微服务架构下的负载均衡:Spring Cloud如何实现高效流量分配

在Spring Cloud中&#xff0c;实现服务的负载均衡&#xff0c;主要是为了让多个服务实例能够均匀分担请求压力&#xff0c;就像把一堆东西分给几个人拿&#xff0c;确保大家都不太累。 假设你开了一个网店&#xff0c;有很多顾客会同时来买东西&#xff08;这就是并发请求&…

L3自动驾驶开始落地,AI交通时代离我们有多远?

2025年&#xff0c;自动驾驶领域迎来了一个重要的里程碑——L3级别自动驾驶技术的逐步落地。据《中国汽车报》报道&#xff0c;多家汽车制造商已获得L3级自动驾驶的量产资质&#xff0c;这意味着车辆能够在特定条件下完全接管驾驶任务&#xff0c;而驾驶员可以在车内进行其他活…

动手学大数据-2常见的查询优化器

目录 什么是查询优化器 查询优化器分类 Top-downOptimizer Bottom-upOptimizer RBO-关系代数 RBO-优化原则 RBO-列裁剪 RBO-谓词下推 RBO-传递闭包 RBO-RuntimeFilter 小结 CBO&#xff08;Cost-basedOptimizer&#xff09; 概念 CBO-统计信息 CBO-统计信息…

ctfshow复现2024ciscn第一场web

2024ciscn第一场 本章内容均在ctfshow复现 图片若显示失败请参考我的blog&#x1f447; ddl08.github.io sanic python污染 源码 from sanic import Sanic from sanic.response import text, html from sanic_session import Session import pydash # pydash5.1.2 ​ ​…

Java快速入门之数组、方法

一、数组 1、数组的概念&#xff1a; 数组指的是一种容器&#xff0c;可以用来存储同种数据类型的多个值&#xff0c;但是数组容器在存储数据的时候&#xff0c;需要结合隐式转换考虑。 例如&#xff1a; 定义了一个int类型的数组&#xff0c;那么boolean、double类型的数据是…

STM32 FreeRTOS任务通知

目录 任务通知的简介 任务通知相关API函数介绍 任务通知的简介 任务通知是 FreeRTOS 中一种用于任务间通信的机制&#xff0c;它允许一个任务向其他任务发送简单的通知或信号&#xff0c;以实现任务间的同步和协作。任务通知通常用于替代二值信号量或事件标志组&#xff0c;提…

Chrome谷歌浏览器如何能恢复到之前的旧版本

升级了谷歌最新版不习惯&#xff0c;如何降级版本 未完待续。。 电脑中的Chrome谷歌浏览器升级到了最新版本&#xff0c;但是有种种的不适应&#xff0c;如何能恢复到之前的旧版本呢&#xff1f;我们来看看操作步骤&#xff0c;而且无需卸载重装。 怎么恢复Chrome 之前版本&a…

前端实习第二个月小结

时间飞快&#xff0c;第一次实习已经过去两个多月&#xff0c;作一些简单的总结和分享。 注&#xff1a;文章整体会比较轻松&#xff0c;提及的经历、经验仅作参考。 一、关于实习/工作内容 1、工作内容 近期做的是管理后台方面的业务&#xff0c;技术栈&#xff1a;前端re…

阿里云 Serverless 助力盟主直播:高并发下的稳定性和成本优化

在直播场景中&#xff0c;阿里云 Serverless 应用引擎 SAE 提供的无缝弹性伸缩与极速部署能力&#xff0c;确保直播间高并发时的流畅体验&#xff0c;降低了我们的运营成本&#xff0c;简化了运维流程。结合阿里云云原生数据库 PolarDB 的 Serverless 能力&#xff0c;实现了数…