建外贸网站需要多少钱/2023年8月份新冠病毒
文章目录
- main函数很普通
- main函数之前调用了什么
- main函数和自定义函数的对比
- 变量名只为人而存在
- goto是循环的本质
- 指针变量
- 指针是一个特殊的数字
- 汇编层面看指针
- 数组和指针
- 数组越界问题
- 低端地址越界
- 高端地址越界
- 引用就是指针
main函数很普通
main函数是第一个被调用的函数吗?在用户视角看来main函数的确程序的入口,但是在CPU视角下,main函数仅仅只是一个普通函数,和用户自定义的其他函数没有任何的区别。
main函数之前调用了什么
Linux环境:
_start->__libc_start_main->main
每一个Linux进程的入口函数都是_start,_start是一段直接由汇编语言编写的函数,它负责的工作就是把程序的命令行参数以及环境变量压入栈中,此时环境变量和参数一起存放在一个数组中
为了把环境变量单独提取出来,_start紧接着会调用__lib_start_main函数构建一张环境变量表,并进行一些全局变量的初始化工作,随后再进入main函数执行用户程序,再main函数退出时进行收尾操作例如全局变量的释放。这么一来,main函数似乎也只不过是一个被调用的函数,它只是默认被注册为用户代码的入口而已,也就是说,用户代码入口不一定非要是main函数
main函数和自定义函数的对比
一直以来我们编写C/C++程序时都是约定俗成地添加一个main函数来启动程序(因为不这么做往往会报错),这种情况一度让不少人认为main函数具有特殊的地位,能够得到CPU的青睐,其实不然,CPU眼中main函数啥也不是就是很普通的函数
int main(){return 0;
}
int func(){return 0;
}
通过汇编观察main和func的区别,会发现它们所对应的汇编指令居然完全一致
main:push rbpmov rbp, rspmov eax, 0pop rbpret
func:push rbpmov rbp, rspmov eax, 0pop rbpret
2个函数所做的操作都是一样的
- 建立函数栈帧 push rbp / mov rbp,rsp
- 将返回值拷入寄存器 mov eax,0
- 释放函数栈帧并返回 pop rbp / ret
gcc有一个命令可以改变用户代码的入口,使得用户指定其他函数作为程序起点
gcc -nostartfiles -efunc test.c
意思是编译test.c不使用系统的标准启动文件,将程序起点设置为func函数;一般不推荐这么做,因为使用标准启动文件代表着你需要自己为func瞻前顾后,这无疑是在自找麻烦
变量名只为人而存在
变量对程序员来说并不陌生,我们无时无刻都在使用变量帮助我们记忆,因为一个好的变量名可以大大提高源代码的可读性;尽管如此,对于可执行文件来说它并不需要存储所谓的变量名(release模式编译链接),CPU只需要知道一个逻辑地址就可进行读写操作,也就是说在发布模式编译链接时所有的变量名都会被转换称逻辑地址。
因此,我们可以给出关于变量的定义:=变量就是逻辑地址的一个别名,它向上以字符串形式以供人阅读记忆,向下被转成地址值供CPU访存
int a=0;
int main(){a=2;return 0;
}
所对应的汇编文件
main:push rbpmov rbp,rspmov DWORD PTR [rip+0x0],0x2 # e <main+0xe>//将立即数0x2写入到rip值偏移量为0的位置,DWORD PTR标识4字节mov eax,0x0pop rbpret
可以看出a=2这条代码所对应的汇编是mov DWORD PTR [rip+0x0],0x2,CPU只需要通过几个逻辑地址相对寻址就可以确定内存的哪个位置需要被赋值为2
goto是循环的本质
虽然说不鼓励在编写C/C++程序时随意的使用goto,但是不代表goto不值得探究,早期的循环其实都是通过goto语句来实现的,只不过随着程序越来越大,过多的goto语句打破了程序的结构性使得源码难以维护,进而衍生出了结构性更强的for、while、do语句,它们都是在底层实现上都继承的goto的机制
void test_for(){for(int i=0;i<10;++i){}
}
void test_while(){int i=0;while(i<10) ++i;
}
void test_do(){int i=0;do{}while(++i<10);
}
void test_goto(){int i=0;goto L1;
L2:if(i<10) goto L1;return ;
L1:++i;goto L2;
}
对应的汇编代码
test_for:
//...mov DWORD PTR [rbp-4], 0jmp .L2
.L3:add DWORD PTR [rbp-4], 1
.L2:cmp DWORD PTR [rbp-4], 9jle .L3
//...
test_while:
//...mov DWORD PTR [rbp-4], 0jmp .L5
.L6:add DWORD PTR [rbp-4], 1
.L5:cmp DWORD PTR [rbp-4], 9jle .L6
//...
test_do:
//...mov DWORD PTR [rbp-4], 0
.L8:add DWORD PTR [rbp-4], 1cmp DWORD PTR [rbp-4], 9jle .L8
//...
test_goto:
//...mov DWORD PTR [rbp-4], 0jmp .L10
.L14:nop
.L10:add DWORD PTR [rbp-4], 1nopcmp DWORD PTR [rbp-4], 9jle .L14
//...
除了标签值不一样外,可以说基本上是一模一样
jmp指令是无条件跳转,对应进入循环体
cmp指令作作比较,add指令对应+1
jle指令是有条件跳转,负责继续or结束循环
指针变量
指针可以说是C语言的精髓所在,正是因为指针,使得C语言称为最灵活的高级语言,它使得用户可以自由的对一个内存区域进行读写(读写是否合法是另一码事),为了更好的理解指针变量,我们把指针变量这个名词拆解为指针+变量,变量上面提过它是地址的别名,指针其实就是一个地址值,因此所谓的定义一个指针变量的本质就是在一块内存空间上写入一个地址值(这和写入一个普通数没有什么区别)
指针是一个特殊的数字
地址值的本质就是数字,只不过它可以被用于访存(解引用),CPU可以先通过读取存放指针的那一块内存得到其中的地址值,再解引用该地址读写内存
汇编层面看指针
int a=1;
int main(){int* p=&a;*p=2;int** pp=&p;*pp=0;return 0;
}
main:
//...mov QWORD PTR [rbp-16], OFFSET FLAT:a //把a的地址写入地址[rbp-16]处,QWORD PTR标识8字节mov rax, QWORD PTR [rbp-16] //读取rbp-16地址处的值放入寄存器rax(a的地址)mov DWORD PTR [rax], 2 //解引用lea rax, [rbp-16]mov QWORD PTR [rbp-8], rax mov rax, QWORD PTR [rbp-8] mov QWORD PTR [rax], 0
//...
无论是几级指针的解引用,本质上都没有什么不同,都是从一块内存中获得另一块内存的位置进行访存
数组和指针
C语言中所有的数组传参最终都会退化成指针传参,因此传入多大的数组,最终在一个函数内部所看到的都是一个大小固定的指针
void fun1(int arr[5]){arr[3]=1;}
void fun2(char arr[100]){arr[3]=1;}
void fun3(double arr[1024]){arr[3]=1;}
汇编代码
fun1:
//...mov QWORD PTR [rbp-8], rdimov rax, QWORD PTR [rbp-8]add rax, 12 //3*4mov DWORD PTR [rax], 1
//...
fun2:
//...mov QWORD PTR [rbp-8], rdimov rax, QWORD PTR [rbp-8]add rax, 3 //3*1mov BYTE PTR [rax], 1
//...
fun3:
//...mov QWORD PTR [rbp-8], rdimov rax, QWORD PTR [rbp-8]add rax, 24 //3*8movsd xmm0, QWORD PTR .LC0[rip]movsd QWORD PTR [rax], xmm0
//...
数组索引操作的本质就是解引用,因此例子中的三个函数等价于
void fun1(int* arr){arr[3]=1;}
void fun2(char* arr){arr[3]=1;}
void fun3(double* arr){arr[3]=1;}
所谓的索引操作只不过是一个偏移量,用于指针的加减操作(指针变量加1减1的跨度取决于指向的类型,如果是int就移动4字节,char就移动1字节,double则是8字节)
数组越界问题
指针作为C语言的精髓,同时也是C语言最危险的一面,原则上一旦获取到地址就可以进行访存,但不能保证目标地址的数据是否可以被安全覆盖,如果一旦不小心将一些重要的内存空间刷新就有可能导致进程崩溃甚至更严重的后果。这种行为称之为野指针非法寻址,所谓野指针就是一个不应该被读写内存空间的地址,野指针最容易出现的场景就是数组越界。
虽然数组越界问题很危险,不过好在随着编译器进步,大部分数组越界问题都能在编译阶段得到拦截。
低端地址越界
void func1(){int a[2];a[1]=1;a[0]=2;a[-1]=3;a[-2]=4;
}
void func2(){int b[4];b[3]=1;b[2]=2;b[1]=3;b[0]=4;
}
int main(){func1();printf("have a good day\n");return 0;
}
很明显func1中存在数组越界访问的问题,但是它可能可以运行不会有段错误(可以看到have a good day,高版本编译器在编译阶段就直接报错),之所以正常运行的原因是因为虽然func1对一个非法区域进行了写入操作,但是碰巧这一块区域没有任何有效数据,所以不会出错
汇编代码
func1:
//...mov DWORD PTR [rbp-4], 1mov DWORD PTR [rbp-8], 2mov DWORD PTR [rbp-12], 3mov DWORD PTR [rbp-16], 4
//...
func2:
//...mov DWORD PTR [rbp-4], 1mov DWORD PTR [rbp-8], 2mov DWORD PTR [rbp-12], 3mov DWORD PTR [rbp-16], 4
//...
如果编译可以通过,通过查看汇编代码可以知道-1、-2索引操作偷偷地拓展了数组的长度使之变成4,并且是向低地址拓展的(rbp保存栈底指针,栈向低地址增长),这种越界称为低端地址越界,它可能不会造成程序崩溃,但大概率会影响到结果正确性(因为func1修改了本不属于它的栈空间,这可能造成其他局部变量数据失效)
高端地址越界
为了更好地就是高端地址越界,需要先稍微了解一下函数栈帧概念,每一个函数有一个栈空间称为栈帧,函数中所需要的局部变量都存储在栈帧中,当被调函数返回时需要销毁栈帧,CPU的指令寄存器恢复至主调函数。因此在建立新的函数栈帧时必须保存当前的指令地址以供后续返回,这个返回地址通过紧邻着被调函数栈帧的栈底。如果被调函数意外的修改了这里的值,就会发生意外(意外地执行恶意代码或段错误);这种越界称为高端地址越界
void evil(){//恶意代码printf("evil\n");exit(1);//让进程意外结束
}
void func1(){int a[2];a[2]=(int)evil; //将返回值设置为一个恶意函数//a[2]=0,将返回值设置为0地址处,这里没有合法指令就报段错误a[1]=1;a[0]=2;
}
int main(){func1();printf("have a good day\n");return 0;
}
(如果编译可以通过)main函数调用func1后进程就结束了,没有输出have a good day,即func1回不到main了,转而走到evil
引用就是指针
C++中引入的引用是对指针操作的简化,但其实只是在语法层面上做了一层封装和少量限制(没有多级引用和空引用,引用二次引用其他对象)
void f1(int* x){*x=1;}
void f2(int& x){x=1;}
void f3(int&& x){x=1;}
汇编代码
f1(int*):
//...建立栈帧mov QWORD PTR [rbp-8], rdimov rax, QWORD PTR [rbp-8]mov DWORD PTR [rax], 1
//...释放栈帧
f2(int&):
//...mov QWORD PTR [rbp-8], rdimov rax, QWORD PTR [rbp-8]mov DWORD PTR [rax], 1
//...
f3(int&&):
//...mov QWORD PTR [rbp-8], rdimov rax, QWORD PTR [rbp-8]mov DWORD PTR [rax], 1
//..
通过汇编可以很明显的看到f1和f2的赋值操作完全一致,引用也是通过获得地址后解引用才能实现对外部变量的修改,左值引用和右值引用在汇编实现上没有什么区别,只不过对于右值引用的修改是针对于一个被延长声明周期的临时对象做修改。
一句话描述引用:引用变量就是一个指针,对于引用变量的修改就是解引用操作
————————————————————————————
相关文章:

底层视角看C语言
文章目录 main函数很普通main函数之前调用了什么main函数和自定义函数的对比 变量名只为人而存在goto是循环的本质指针变量指针是一个特殊的数字汇编层面看指针 数组和指针数组越界问题低端地址越界高端地址越界 引用就是指针 main函数很普通 main函数是第一个被调用的函数吗&…...

【点云学习笔记】——分割任务学习
3D点云实例分割 vs 3D点云语义分割 1. 功能对比 代码1(实例分割):用于3D点云中的实例分割任务,其目标是将点云中的物体分割成独立的实例。每个实例可能属于相同类别但需要被分开,比如在自动驾驶中的多个行人、汽车&am…...

Qt——窗口
一.窗口概述 Qt 窗口是通过 QMainWindow 类来实现的。 QMainWindow是一个为用户提供主窗口程序的类,继承QWidget类,并且提供一个预定义的布局。包含一个菜单栏(menu bar),多个工具栏(tool bars࿰…...

InfluxDB性能优化指南
1. 引言 1.1 InfluxDB的简介与发展背景 InfluxDB是一个开源的时间序列数据库(TSDB),由InfluxData公司开发,专门用于处理高频率的数据写入和查询。其设计初衷是为物联网、应用程序监控、DevOps和实时分析等场景提供一个高效的存储…...

负载均衡式在线oj项目开发文档2(个人项目)
judge模块的框架 完成了网页渲染的功能之后,就需要判断用户提交的代码是否是正确的,当用户点击提交之后,就会交给路由模块的/judge模块,然后这个路由模块就需要去调用jude模块了,也就是需要一个新的jude模块ÿ…...

ssm081高校实验室管理系统的设计与实现+vue(论文+源码)_kaic
毕 业 设 计(论 文) 题目:高校实验室管理系统的设计与实现 摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很…...

GitLab基于Drone搭建持续集成(CI/CD)
本文介绍了如何为 Gitee 安装 Drone 服务器。服务器打包为在 DockerHub 上分发的最小 Docker 映像。 1. 准备工作 创建OAuth应用 创建 GitLab OAuth 应用。Consumer Key 和 Consumer Secret 用于授权访问极狐GitLab 资源。 ps:授权回调 URL 必须与以下格式和路径匹配&…...

用GPB外链打造长期稳定的SEO优势!
很多人在谈外链时,总喜欢纠结是追求数量还是追求质量。其实,最理想的策略是两者兼顾。而在这其中,GPB外链可以说是长期SEO提升的“法宝”。为什么这么说?因为GPB外链不仅保证了高质量,还附带了与网站主题高度相关的原创…...

第11章 内连接与外连接
一、介绍内连接与外连接 (1)内连接与外连接介绍 1、内连接:合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行。 2、外连接:: 两个表在连接过程中除了返回满足连接条件的行以外还返回左(…...

C++ 游戏开发:打造高效、性能优越的游戏世界
在游戏开发领域,C 一直是最受欢迎的编程语言之一。其高效的内存管理和对硬件的底层控制,使得 C 成为开发高性能游戏的首选语言。从大型 3D 游戏引擎到独立游戏的制作,C 在游戏开发中发挥了不可替代的作用。 本文将带你了解 C 在游戏开发中的…...

太速科技-440-基于XCVU440的多核处理器多输入芯片验证板卡
基于XCVU440的多核处理器多输入芯片验证板卡 一、板卡概述 本板卡系我司自主研发的基于6U CPCI处理板,适用于多核处理器多输入芯片验证的应用。芯片采用工业级设计。 基于XCVU440T的多核处理器多输入芯片验证板卡基于6U CPCI架构,是单机中的一个…...

澳鹏通过高质量数据支持 Onfido 优化AI反欺诈功能
“Appen 在 Onfido 的发展中发挥了至关重要的作用,并已成为我们运营的重要组成部分。我们很高兴在 Appen 找到了可靠的合作伙伴。” – Onfido 数据和分析总监 Francois Jehl 简介:利用人工智能和机器学习增强欺诈检测 在当今日益数字化的世界ÿ…...

基于ECS实例搭建Hadoop环境
环境搭建: 【ECS生长万物之开源】基于ECS实例搭建Hadoop环境-阿里云开发者社区 搭建Hadoop环境_云服务器 ECS(ECS)-阿里云帮助中心 Hadoop入门基础(二):Hadoop集群安装与部署详解(超详细教程)࿰…...

关于vue如何监听route和state以及各自对应的实际场景
一、监听route 场景:监听浏览器地址栏分页参数的变化 // 注意 newPageNum和 oldPageNum是 string类型 $route.query.pageNum(newPageNum, oldPageNum) {if (newPageNum ! oldPageNum && newPageNum ! this.pageNum.toString()) {this.handleCurrentChange(p…...

【计网不挂科】计算机网络期末考试(综合)——【选择题&填空题&判断题&简述题】完整题库
前言 大家好吖,欢迎来到 YY 滴计算机网络 系列 ,热烈欢迎! 本章主要内容面向接触过C的老铁 本博客主要内容,收纳了一部门基本的计算机网络题目,供yy应对期中考试复习。大家可以参考 欢迎订阅 YY滴其他专栏!…...

Linux(CentOS)设置防火墙开放8080端口,运行jar包,接收请求
1、查看防火墙状态 systemctl status firewalld 防火墙开启状态 2、运行 jar 包,使用8080端口 程序正常启动 3、使用 postman 发送请求,失败 4、检查端口是否开放(需更换到 root 用户) firewall-cmd --zonepublic --query-por…...

对比:生成对抗网络(GANs)和变分自编码器(VAEs)
以下是生成对抗网络(GANs)和变分自编码器(VAEs)的详细介绍、区别、优缺点的对比表: 项目生成对抗网络(GANs)变分自编码器(VAEs)定义GANs 是一种生成模型,通过…...

sqlserver inner join on 条件是包含 怎么写
LEFT JOIN T_Customer tc on CHARINDEX(tbd.CluePhoneNumber,tc.u_phone)>0...

开源 AI 智能名片 S2B2C 商城小程序在微商内容展示中的应用与价值
摘要:本文围绕微商在社群和朋友圈这一“店面”的内容展示展开深入讨论,剖析展示对产品的热爱、产品真实反馈和代理反馈的重要意义,并详细阐述开源 AI 智能名片 S2B2C 商城小程序如何助力微商优化这些内容展示,从而提升微商营销效果…...

Codeforces Round 984 (Div. 3) (A~E)
文章目录 A. Quintomania思路code B. Startup思路code C. Anya and 1100思路code D. I Love 1543思路code E. Reverse the Rivers思路code https://codeforces.com/contest/2036 A. Quintomania 思路 签到题,直接模拟即可 code void solve(){int n;cin >>…...

pytorch3d报错:RuntimeError: Not compiled with GPU support.
目录 解决方法:编译之前:加上指令: 解决方法:pytorch3d 安装命令(ubuntu),成功!!! 测试代码: FORCE_CUDA1 works for me. Thanks! args (point…...

软考中级-软件设计师 数据结构与算法
文章目录 考点数据结构基础线性结构非线性结构 常见算法排序算法查找算法递归算法分治算法动态规划贪心算法 复杂度分析 考点 在软考中,数据结构与算法的考点主要集中在以下方面: 基本概念:掌握各类数据结构的定义、特点和应用场景。常用算…...

关于CSS表达使中使用的 max() 函数
定义: max() 函数:它会返回括号中给定的值中的最大值。 比如,width: max(250px, 25vw);-------它比较 250px 和 25vw,然后选择其中的较大值作为元素的宽度。 让我们逐步解析这个表达式: 250px:表示一个…...

51单片机教程(八)- 数码管的静态显示
1、项目分析 使用数码管显示指定的字符、数字和符号。 2、技术准备 1、显示器及其接口 单片机系统中常用的显示器有: 发光二极管LED(Light Emitting Diode)显示器、液晶LCD(Liquid Crystal Display)显示器、CRT显…...

案例精选 | 河北省某检察院安全运营中异构日志数据融合的实践探索
河北省某检察院是当地重要的法律监督机构,肩负着维护法律尊严和社会公平正义的重要职责。该机构依法独立行使检察权,负责对犯罪行为提起公诉,并监督整个诉讼过程,同时积极参与社会治理,保护公民权益,推动法…...

clickhouse自增id的处理
msyql 中创建数据表的时候可以通过AUTO_INCREMENT 来实现,clickhouse中可以通过其他方式来处理 一、 默认值 创建表时可以实用默认值,该列值可以自动递增。如下所示 CREATE TABLE my_table ( id UInt32 DEFAULT IDENTITY(AUTO_INCREMENT), name Strin…...

国内读新加坡公立大学在职博士是一种怎样的体验?还中文授课
国内读新加坡公立大学在职博士是一种怎样的体验?还中文授课 在国内享受国际化教育体系,这样的优势无论在学术和职业发展上,还是在个人综合素质和拓宽国际视野方面,都是无法抗拒的诱惑。当下这所新加坡公立大学就给了国内在职人员…...

linux 配置core
在Linux系统中,当一个程序崩溃时,系统可以生成一个名为"core dump"的文件。这个文件包含了程序崩溃时的内存映像,可以用来调试和确定程序崩溃的原因。生成core dump文件的功能是由内核配置的,可以通过多种方式来控制这个…...

postcss-loader运行报错
解决方案: 1、检查postcss和postcss-cssloader相关依赖 npm list postcss postcss-loader 2、原因: 你的依赖中存在 PostCSS 的版本冲突: 3、结局方案: 升级整个工具链到新版本(推荐): npm…...

智能存储解决方案:探索 TDengine 的多级存储功能
在当今数据驱动的时代,如何高效地存储和管理海量数据已成为企业面临的一大挑战。为了应对这一需求,TDengine Enterprise 不仅支持使用对象存储(S3),还早已引入了独特的多级存储功能。这一功能不仅能够降低存储成本&…...