当前位置: 首页 > news >正文

函数栈帧详解

写在前面

这个模块临近C语言的边界,学起来需要一定的时间,不过当我们知道这些知识后,在C语言函数这块我们看到的不仅仅是表象了,可以真正了解函数是怎么调用的。不过我的能力有限,下面的的知识若是不当,还请各位斧正。

知识点储备

  • 初步了解函数( 这里的所说的函数我们默认为自定义函数)
  • 了解C程序地址空间
  • 基本的寄存器
  • 知道一些汇编语言

函数的概念

函数大家应该都很熟悉了,这里就不细说了。我们看看就行

ret_type fun_name(para1, * )
{statement;  //语句项
}ret_type  返回类型
fun_name  函数名
para1     函数参数  

虚拟地址空间

我们一直说 :“全局变量的生命周期是所在的整个程序”、“static修饰的变量的生命周期变长了”、以及“最重要的临时变量出函数就要被销毁”。不过我们要知道这是因为什么.在C语言中我们所创建的每一个变量都会有自己空间的的存储类别,就比如汽车一般不会停在高楼那样,每一个事物都会有自己的集合,计算机的数据存储也是如此,我们这看一下.

在计算机中,我们把内存分为若干的区间(这里暂时这样理解),每一个区间保存特定的数据,我们先来看C语言程序地址空间,也是虚拟地址空间.

image-20230301103430508

上面不做解析,我们在后面会学到,大家把这个图给记住就可以了.看一下代码,来验证一下.可以看出局部变量存储在栈上且栈空间是沿着向低地址方向开辟的,堆区与之相反.

#include<stdio.h>                                                                             
#include<stdlib.h>int g_val1 = 10;
int g_val2 = 10;
int g_val3;
int g_val4;int main()
{const char* str = "abcdef";printf("code: %p\n", main);printf("read only : %p\n", str);printf("init g_val1 : %p\n", &g_val1);printf("init g_val2 : %p\n", &g_val2);printf("uninit g_val2 : %p\n", &g_val3);printf("uninit g_val2 : %p\n", &g_val4);char* p1 = (char*)malloc(sizeof(char*) * 10);char* p2 = (char*)malloc(sizeof(char*) * 10);printf("heap addr : %p\n", p1);printf("heap addr : %p\n", p2);printf("stack addr : %p\n", &str);printf("stack addr : %p\n", &p1);printf("stack addr : %p\n", &p2);return 0;
}

image-20230301103628715

寄存器

函数的调用与CPU中的寄存器有很大关系,下面有一些基本知识

  • eax:通用寄存器,保留临时数据,常用于返回值
  • ebx:通用寄存器,保留临时数据
  • ebp:栈底寄存器
  • esp:栈顶寄存器
  • eip:指令寄存器,保存当前指令的下一条指令的地址,衡量走到了那一步

简单汇编语言

这里是一些常见的汇编语言的指令,这里先和大家列出,后面我们产看汇编语言的时候,直接回来看这些语言是什么意思.

  • mov:数据转移指令
  • push:数据入栈,同时esp栈顶寄存器也要发生改变
  • pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变
  • sub:减法命令
  • add:加法命令
  • call:函数调用,1. 压入返回地址 2. 转入目标函数
  • jump:通过修改eip,转入目标函数,进行调用
  • ret:恢复返回地址,压入eip,类似pop eip命令

函数栈帧

看了这么多知识,我们一定会感到很是枯燥,觉得这和函数栈帧一点关系都没有,不要着急,下面就开始我们正式的内容。这里为了便于理解,我们这么看栈的空间,我们就多画些图片

我们知道 main函数也是一个函数,它也是能够被调用,所以main函数也会形成栈帧。

image-20230301105834840

样例代码

下面我们用一个很简单的代码来和大家简单的谈函数的原理,代码虽小,但是却能反应问题.

int MyAdd(int a, int b)
{int c = a + b;return c;
}int main()
{int x = 0xA;int y = 0xB;int z = 0;z = MyAdd(x, y);printf("z = %d\n", z);return 0;
}

我们直接开始调试,然后转到反汇编,打开寄存器.
在这里插入图片描述

汇编代码

我将汇编代码复制下来,我们一步一步分析这些东西.

int main()
{
00821E40  push        ebp  
00821E41  mov         ebp,esp  
00821E43  sub         esp,0E4h  
00821E49  push        ebx  
00821E4A  push        esi  
00821E4B  push        edi  
00821E4C  lea         edi,[ebp-24h]  
00821E4F  mov         ecx,9  
00821E54  mov         eax,0CCCCCCCCh  
00821E59  rep stos    dword ptr es:[edi]  
00821E5B  mov         ecx,82C003h  
00821E60  call        0082130C  int x = 0xA;
00821E65  mov         dword ptr [ebp-8],0Ah  int y = 0xB;
00821E6C  mov         dword ptr [ebp-14h],0Bh  int z = 0;
00821E73  mov         dword ptr [ebp-20h],0  z = MyAdd(x, y);
00821E7A  mov         eax,dword ptr [ebp-14h]  
00821E7D  push        eax  
00821E7E  mov         ecx,dword ptr [ebp-8]  
00821E81  push        ecx  
00821E82  call        008211E5  
00821E87  add         esp,8  
00821E8A  mov         dword ptr [ebp-20h],eax  printf("z = %d\n", z);
00821E8D  mov         eax,dword ptr [ebp-20h]  
00821E90  push        eax  
00821E91  push        827BCCh  
00821E96  call        008213A2  
00821E9B  add         esp,8  return 0;
00821E9E  xor         eax,eax  
}
00821EA0  pop         edi  
00821EA1  pop         esi  
00821EA2  pop         ebx  
00821EA3  add         esp,0E4h  
00821EA9  cmp         ebp,esp  
00821EAB  call        00821235  
00821EB0  mov         esp,ebp  
00821EB2  pop         ebp  
00821EB3  ret  

这里把寄存器再次简单的说一下.

  • ebp指向栈底
  • esp指向栈顶
  • eip指向下一个即将执行的地址 还未执行

main函数栈帧形成

注意,main函数也是有栈帧的,这里我们默认mian函数栈帧已经创建完成了,只看MyAdd函数栈帧的形成,他们都是一样的,我们希望以最小的成本来和大家经行分析.

代码逻辑

下面开始main函数内部大代码逻辑,这是局部变量空间的开辟.

int x = 0xA;
01011E65  mov         dword ptr [ebp-8],0Ah //在ebp-8处在开辟一个空间,将x的值放进去

image-20230301105806031

	int y = 0xB;01011E6C  mov      dword ptr [ebp-14h],0Bh  //在ebp-14处在开辟一个空间,将y的值放进去

image-20230301110453899

		int z = 0;00821E73  mov   dword ptr [ebp-20h],0  //在ebp-20处在开辟一个空间,将z的值放进去 

image-20230301110638966

我们可以通过汇编语言看出可以看出,x、y、z 的空间是不连续的 ,这是VS保护机制, 防止一些程序员猜测对应的地址,不过一些老的编译器是不会这么做的,这里不要求我们记忆.

形参拷贝

下面汇编语言继续往下面走,我们此时遇到了函数的调用,那么编译器首先要做的就是形参的拷贝.

这就话的意思是把ebp-14(也就是y) 赋值给eax eax是一个临时的寄存器,保留临时数据,常用于返回值,这里的寄存器都是一个临时的容器,方便

	z = MyAdd(x, y);
00821E7A  mov         eax,dword ptr [ebp-14h]  
在这里插入图片描述

此时编译器会做下面动作.把eax里面的数据推送到栈中.push命令将eax的值放入栈中,同时栈顶esp的位置发生变化,变化的大小是4个字节,因为y是int型

00821E7D  push        eax  

image-20230301111820019

此时我们更新一下我们地址空间的总图,把栈顶寄存器的值保存一下.

image-20230301112017071

下面同理,我们也罢x的值进行拷贝一番,把ebp-8(也就是x) 赋值给ecx,然后推送到栈帧中.

    00821E7E  mov         ecx,dword ptr [ebp-8]  00821E81  push        ecx  
在这里插入图片描述

我们还是要更新一下栈顶的位置发生变化.
image-20230301112249321

下面我们开始总结一下上面的过程.

  • 编译器首先为main函数开辟栈帧,然后按照顺序进行变量空间的开辟和初始化
  • 遇到调用函数时,临时变量的形成(实参的临时拷贝)在函数调前就完成了
  • 形参实例化的顺序是从右向左依次形成的,这个有很大的用处,只不过在C++中会体现
  • 形参的空间是紧邻的,毕竟形参的变量空间的创建伴随着栈顶的变化.

函数调用

下面我们正式开始函数的调用工作,此时涉及很多的东西.这里先说一下call命令的作用

  • 压入返回地址 (最重要的)
  • 转入目标函数

我们说压入返回地址,此时谁是返回地址呢call命令的下一条命令的地址. 为什么要压入?根本原因是函数调用完毕,我们要放回mian函数执行其他的代码,需要返回就需要一个回来的坐标.

00821E82  call        008211E5

在这里插入图片描述

此时我们一跳这个指令,那么此时编译器会做两个事情,把call指令的地址压入栈顶,顺便修改esp,让后进入call 的地址处.

在这里插入图片描述

下面开始jump指令,这是一个跳转指令,把寄存器eip的数据从当前指令的地址改变成jump的地址,我们把这个目的地址认为是我们调用函数的地址,此时转入目标函数.

image-20230301115914240

这里存在一个问题, esp 也就是栈顶的指针,为何发生变化了?这是因为call指令的第一个作用,至于改变了多少,我们先不关心,只需要知道上升的空间里面是保存一个指针的,那么就是4个或者8个字节.

image-20230301192710989

MyAdd函数栈帧的形成

我们继续开始下面最关键的一个步骤,由于我的不小心,把代码从调试状态退出来了,此时这重新进入调试,那么汇编语言中的相关地址会发生变化,不过代码的逻辑是不会发生变化的,我们还是把汇编语言进行复制出来,这里只看MyAdd函数的汇编语言.

int MyAdd(int a, int b)
{
001E2EC0  push        ebp  
001E2EC1  mov         ebp,esp  
001E2EC3  sub         esp,0CCh  
001E2EC9  push        ebx  
001E2ECA  push        esi  
001E2ECB  push        edi  
001E2ECC  lea         edi,[ebp-0Ch]  
001E2ECF  mov         ecx,3  
001E2ED4  mov         eax,0CCCCCCCCh  
001E2ED9  rep stos    dword ptr es:[edi]  
001E2EDB  mov         ecx,1EC003h  
001E2EE0  call        001E130C  int c = 0;
001E2EE5  mov         dword ptr [ebp-8],0  c =a + b;
001E2EEC  mov         eax,dword ptr [ebp+8]  
001E2EEF  add         eax,dword ptr [ebp+0Ch]  
001E2EF2  mov         dword ptr [ebp-8],eax  return c;
001E2EF5  mov         eax,dword ptr [ebp-8]  
}
001E2EF8  pop         edi  
001E2EF9  pop         esi  
001E2EFA  pop         ebx  
001E2EFB  add         esp,0CCh  
001E2F01  cmp         ebp,esp  
001E2F03  call        001E1235  
001E2F08  mov         esp,ebp  
001E2F0A  pop         ebp  
001E2F0B  ret 

压入栈底

此时编译器首先要做的就是把原本栈底寄存器ebp中的数据压入到栈中,同时栈顶也发生变化

00821740  push        ebp  

这条命令是将ebp(也就是栈底)的内容压入栈中,同时栈顶也发生变化
image-20230301193616424

修改栈底

下面开始数据转移指令,该命令的意思是将esp的内容覆盖到ebp中,也就是此时栈底变得和栈顶一样了,该过程没有通过内存,直接通过CPU. 那么我们可能会发出疑惑,那main函数栈底怎么办,是不是找不回来了?实际上不是的,上一步我们不是把栈底的内容给保存了吗!

00821741  mov         ebp,esp 

image-20230301194044842

修改栈顶

下面我们开始计算新的栈顶就是在哪里了,根据sub指令,可以计算出新的栈顶在哪里.该命令的意思是esp减去一定的值,结果放在esp中到这里我们已经简单的形成了MyAdd函数的栈帧了

00821743  sub         esp,0CCh//0CCh 的大小和你定义的函数的规模有关

image-20230301194506718

代码逻辑

下面我们开始函数内部的代码逻辑,都是很简单的.这和main的变量开辟一样

int c = 0;
001E2EE5  mov         dword ptr [ebp-8],0  //在ebp-8处在开辟一个空间,将c的值放进去

image-20230301194817461

下面我们开始一步步分析后面的逻辑,注意看,我们上面形参的拷贝都是把数据放在了寄存器中,此时要是想要使用我们形式参数的数据,那么需要编译器手动的取出.

c =a + b;
001E2EEC  mov         eax,dword ptr [ebp+8]  
001E2EEF  add         eax,dword ptr [ebp+0Ch]  
001E2EF2  mov         dword ptr [ebp-8],eax 

先例解释这一条,把ebp+8放在eax中.那么ebp+8是多少呢?答案就是我们的x值的拷贝

001E2EEC  mov         eax,dword ptr [ebp+8]  

image-20230301195651443

同理,这个命令是将ebp+0Ch的内容和eax加起来放到eax中.ebp+0Ch就是y值的拷贝

001E2EEF  add         eax,dword ptr [ebp+0Ch] 

image-20230301195843234

这里是将eax写入到ebp-8,也就是c中,就是加法计算的结果.

001E2EF2  mov         dword ptr [ebp-8],eax 

image-20230301200133091

返回值

其中我们函数栈帧是如何得到的已经了解了,我们来看函数栈帧的最后的一部分.

保存返回值

我们把计算出的结果保存到寄存器中,为了后面的使用

001E2EF5  mov         eax,dword ptr [ebp-8]  

image-20230301200656698

释放栈帧

此时我们函数已经结束了,现在是函数的后期处理工作,包含资源的清理.把ebp 覆盖到esp,也就是收缩栈顶到栈底,这一步可以称为“释放栈帧”,注意函数栈帧的释放可不是空间的释放,只不过是它变成无效,也就是释放栈帧是不等于释放空间的.

001E2F08  mov         esp,ebp  

image-20230301200918352

弹栈

此时发生了弹栈操作,将main函数的栈底放在ebp中,esp内容发生改变.记住了我们在开辟函数栈帧前已经把main函数栈底的地址压入栈中的,此时拿到就可以了.

001E2F0A  pop         ebp  // pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变

image-20230301201210296

注意,此时我们还是处于MyAdd汇编语言的执行部分,此时我们应该返回原本mian函数调用函数的下一个地址,此时我们需要拿到我们曾经的保存在栈中的地址,压入到eip,要知道eip是编译器下一条要到的地址,esp内容改变

001E2F0B  ret  //类似pop eip命令

image-20230301202702935

释放空间

注意此时我们已经出了MyAdd函数的汇编语言了,已经开始了函数调用的后续工作,例如此时要释放临时拷贝的变量的空间,意思是esp+8放在esp中,也就是修改栈顶.

	z = MyAdd(x, y);
00AC1440  add         esp,8  
00AC1443  mov         dword ptr [z],eax  

image-20230301201756600

我们开始继续执行函数调用后的代码,将eax的值放到ebp-20,也就是z,后面mian函数的栈帧的销毁我也就不分析了,逻辑是一样的.

001E1E8A  mov         dword ptr [ebp-20h],eax 

总结

我来一个简单测总结,这个博客只要求大家理解就可以了.

  • 函数的的栈帧是由编译器决定的,C语言中很多数据类型,那么编译器有能力知道所有类型变量的大小。

  • push进去的变量的空间是连续的,所谓的push进入额变量就是 形参拷贝,压入返回值,压入main栈底的操作是连续的

  • 0CCh 的大小和你定义的函数的规模有关,编译器会自动计算.

我们看到了push变量空间是连续的,也就是我们可以通过形参的地址来修改另外的地址.

void MyAdd(int a, int b)
{printf("before: b = %d\n", b);*(&a + 1) = 100;printf("after : b = %d\n", b);
}int main()
{int x = 0;int y = 0;MyAdd(x,y);return 0;
}

image-20230301222008329

那么此时也会做一些更加大胆的事情,例如修改mian函数的栈底和修改放回值,这就会让我们代码不能继续mian函数后续代码了,不过现在在VS是非常做测试了,它做的安全性更加强了.

如果面试官问你你可以讲解一下函数栈帧的底层是什么,可以简单的说一下流程吗?这里我给出下面自己的一个答案,注意回答问题的时候一定要说是自己根据观察VS系列编译器理解的,C语言中的函数栈帧创建流程通常包括以下几个步骤:

  1. 函数参数被压入栈中。在调用函数之前,函数的参数被压入栈中,按照从右到左的顺序进行压栈,
  2. 函数被调用时,系统会将函数的返回地址压入栈中,同时将栈顶指针(Stack Pointer,SP)减少相应的大小,顺便把mian函数的栈底压入栈中
  3. 函数局部变量被压入栈中。函数局部变量也是在栈中分配的,同样按照从右到左的顺序进行压栈。当函数返回时,这些局部变量所占用的栈空间也将被释放。
  4. 在执行函数体之前,为函数创建栈空间。调整栈指针的大小通常是在编译时计算出来的。
  5. 进行函数体的逻辑运算,包含变量的空间创建,使用形参的过程,返回值会被保存在一个寄存器中
  6. 释放栈帧,弹栈,返回地址被弹出栈中,释放形参的空间,调整栈顶,继续执行函数调用后面的代码.

相关文章:

函数栈帧详解

写在前面 这个模块临近C语言的边界&#xff0c;学起来需要一定的时间&#xff0c;不过当我们知道这些知识后&#xff0c;在C语言函数这块我们看到的不仅仅是表象了&#xff0c;可以真正了解函数是怎么调用的。不过我的能力有限&#xff0c;下面的的知识若是不当&#xff0c;还…...

Spring 事务(编程式事务、声明式事务@Transactional、事务隔离级别、事务传播机制)

文章目录1. 事务的定义2. Spring 中事务的实现2.1 MySQL 中使用事务2.2 Spring 中编程式事务的实现2.3 Spring 中声明式事务2.3.1 声明式事务的实现 Transactional2.3.2 Transactional 作用域2.3.3Transactional 参数设置2.3.4 Transactional 异常情况2.3.5 Transactional 工作…...

车载技术——Window Display之surface的绘制过程与原理

一、Surface 概述 OpenGL ES/Skia定义了一组绘制接口的规范&#xff0c;为什么能够跨平台&#xff1f; 本质上需要与对应平台上的本地窗口建立连接。也就是说OpenGL ES负责输入了绘制的命令&#xff0c;但是需要一个 “画布” 来承载输出结果&#xff0c;最终展示到屏幕。这个…...

2023年全国最新工会考试精选真题及答案10

百分百题库提供工会考试试题、工会考试预测题、工会考试真题、工会证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 51.&#xff08;&#xff09;是企业工会的权力机关&#xff0c;每年召开一至两次会议。 A.会员大会 B.会…...

pytorch-复现经典深度学习模型-LeNet5

Neural Networks 使用torch.nn包来构建神经网络。nn包依赖autograd包来定义模型并求导。 一个nn.Module包含各个层和一个forward(input)方法&#xff0c;该方法返回output。 一个简单的前馈神经网络&#xff0c;它接受一个输入&#xff0c;然后一层接着一层地传递&#xff0c;…...

【C++】类和对象(上)

文章目录对象的介绍类的介绍类的两种定义方式类的访问限定符及封装访问限定符封装类的作用域类的实例化类的对象模型对象的介绍 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题&#xff1b;   C是基于面向…...

工作中责任链模式用法及其使用场景?

前言 笔者是金融保险行业&#xff0c;有这么一种场景&#xff0c;业务员录完单后提交核保&#xff0c;这时候系统会对保单数据进行校验&#xff0c;如不允许手续费超限校验&#xff0c;客户真实性校验、费率限额校验等等&#xff0c;当校验一多时&#xff0c;维护起来特别麻烦…...

三八女神节有哪些数码好物?2023年三八女神节数码好物清单

2023年的三八女神节就快到了&#xff0c;大家还在烦恼&#xff0c;不知道有哪些数码好物&#xff1f;在此&#xff0c;我来给大家分享几款三八女神节实用性强的数码好物&#xff0c;一起来看看吧。 一、蓝牙耳机&#xff1a;南卡小音舱 参考价&#xff1a;239 推荐理由&…...

FairGuard-Windows加固工具版本更新日志

FairGuard-Windows加固工具1.2.2版本更新日志&#xff1a; ■ 增加Unity Resources资源加密的支持; ■ 增加单独Assetbundle资源加密&#xff0c;并同时支持压缩包和文件夹作为输入的方式; ■ 增加对游戏原文件夹加固的支持; Windows加固方案介绍 FairGuard专为游戏量身定…...

基于RT-Thread完整版搭建的极简Bootloader

项目背景Agile Upgrade: 用于快速构建 bootloader 的中间件。example 文件夹提供 PC 上的示例特性适配 RT-Thread 官方固件打包工具 (图形化工具及命令行工具)使用纯 C 开发&#xff0c;不涉及任何硬件接口&#xff0c;可在任何形式的硬件上直接使用加密、压缩支持如下&#xf…...

3.flinkDateStreamAPI介绍env与source

执行环境 Flink可以在不同的环境上下文中运行.可以本地集成开发环境中运行也可以提交到远程集群环境运行. 不同的运行环境对应的flink的运行过程不同,需要首先获取flink的运行环境,才能将具体的job调度到不同的TaskManager 在flink中可以通过StreamExecutionEnvironment类获取…...

$ 2 :数据类型

1.数据类型 1.1基本类型 a、整型int b、浮点型float c、字符型char 1.2构造类型 a、数组[ ] b、结构体struct 1.3指针类型 * 1.4空类型(void) 2.关键字 autoconstdoublefloatintshortstructunsignedbreakcontinueelseforlongsignedswitchvoidcasedefaultenumgotoregistersiz…...

类和对象 - 上

本文已收录至《C语言》专栏&#xff01; 作者&#xff1a;ARMCSKGT 目录 前言 正文 面向过程与面向对象 面向过程的解决方法 面向对象的解决方法 面向对象的优势 类的引入 早期C类的实现 class定义类 class定义规则 类成员的两种定义方式 类的访问限定符及封装 访…...

补档:红黑树代码实现+简略讲解

红黑树讲解和实现1 红黑树介绍1.1 红黑树特性1.2 红黑树的插入1.3 红黑树的删除2 完整代码实现2.1 rtbtree.h头文件2.2 main.c源文件1 红黑树介绍 红黑树( Red-Black tree&#xff0c;简称RB树)是一种自平衡二叉查找树&#xff0c;是计算机科学中常见的一种数据结构&#xff0c…...

FirePower X2 14.0.1 for RAD Studio Alexandria

介绍 FirePower X2 FirePower X2 集成了 RAD Studio 11.0 Alexandria 中的新功能&#xff0c;并预览了我们的新特色组件 TwwDataGrouper。 FirePower X2 还允许您为 Apple 的新 M1 芯片构建应用程序&#xff0c;这样您就可以进一步利用 M1 芯片来提高本机应用程序的性能&#x…...

二十九、MongoDB 恢复数据( mongorestore )

MongoDB mongorestore 脚本命令可以用来恢复备份的数据 语法 MongoDB mongorestore 命令脚本语法如下 $ mongorestore -h <hostname><:port> -d dbname <path> 参数说明 -h <:port>, -h<:port> MongoDB 所在服务器地址&#xff0c;默认为 l…...

【数据分析】缺失数据如何处理?pandas

本文目录1. 基础概念1.1. 缺失值分类1.2. 缺失值处理方法2. 缺失观测及其类型2.1. 了解缺失信息2.2. 三种缺失符号2.3. Nullable类型与NA符号2.4. NA的特性2.5. convert_dtypes方法3. 缺失数据的运算与分组 3.1. 加号与乘号规则3.2. groupby方法中的缺失值4. 填充与剔除4.1. fi…...

嵌入式开发--STM32H750VBT6开发中,新版本CubeMX的时钟问题,不能设置到最高速度480MHZ

嵌入式开发–STM32H750VBT6开发中&#xff0c;新版本CubeMX的时钟问题&#xff0c;不能设置到最高速度480MHZ 问题描述 之前开发的项目&#xff0c;开发环境是CubeMX6.6.1&#xff0c;H7系列的支持包版本是1.10.0。跑得没问题&#xff0c;最近需要对项目做修改&#xff0c;同…...

一文读懂PaddleSpeech中英混合语音识别技术

语音识别技术能够让计算机理解人类的语音&#xff0c;从而支持多种语音交互的场景&#xff0c;如手机应用、人车协同、机器人对话、语音转写等。然而&#xff0c;在这些场景中&#xff0c;语音识别的输入并不总是单一的语言&#xff0c;有时会出现多语言混合的情况。例如&#…...

问题三十四:傅立叶变换——高通滤波

高通滤波器是一种可以通过去除图像低频信息来增强高频信息的滤波器。在图像处理中&#xff0c;高通滤波器常常用于去除模糊或平滑效果&#xff0c;以及增强边缘或细节。在本篇回答中&#xff0c;我们将使用Python和OpenCV实现高通滤波器。 Step 1&#xff1a;加载图像并进行傅…...

flink 键控状态(keyed state)

github开源项目flink-note的笔记。本博客的实现代码都写在项目的flink-state/src/main/java/state/keyed/KeyedStateDemo.java文件中。 项目github地址: github 1. flink键控状态 flink键控状态是作用与flink KeyedStream上的,也就是说需要将DataStream先进行keyby之后才能使…...

【ChatGPT】sqlachmey 多表连表查询语句

感受下科技带来的魅力&#xff0c;这篇文章是通过ChatGPT自动生成的&#xff0c;不得不说技术强大!!! 在SQLAlchemy中进行多表连接查询可以使用join()方法或join()函数&#xff0c;具体用法如下&#xff1a; join()方法 join()方法可以在SQLAlchemy ORM中的查询中使用。假设…...

win11 系统登录问题,PIN 设置问题

我的电脑配置是华为MateBook X Pro 12&#xff0c;i7处理器&#xff0c;16G&#xff0c;1T&#xff0c;win11 系统通过微软账户登录&#xff0c;下午一直登录不进去&#xff0c;网络能连外网&#xff0c;分析应该是连微软服务器不行。连续登录几十次&#xff0c;偶尔可能有一次…...

数据结构六大排序

1.插入排序 思路&#xff1a; 从第一个元素开始认为是有序的&#xff0c;去一个元素tem从有序序列从后往前扫描&#xff0c;如果该元素大于tem&#xff0c;将该元素一刀下一位&#xff0c;循环步骤3知道找到有序序列中小于等于的元素将tem插入到该元素后&#xff0c;如果已排序…...

快速生成QR码的方法:教你变成QR Code Master

目录 简介: 具体实现步骤&#xff1a; 一、可以使用Python中的qrcode和tkinter模块来生成QR码。以下是一个简单的例子&#xff0c;演示如何在Tkinter窗口中获取用户输入并使用qrcode生成QR码。 1&#xff09;首先需要安装qrcode模块&#xff0c;可以使用以下命令在终端或命令…...

tensorflow1.14.0安装教程--保姆级

//方法不止一种&#xff0c;下面仅展示一种。 注&#xff1a;本人电脑为win11&#xff0c;anaconda的python版本为3.9&#xff0c;但tensorflow需要python版本为3.7&#xff0c;所以下面主要阐述将python版本改为3.7后的安装过程以及常遇到的问题。 1.首先电脑安装好anaconda…...

AcWing算法提高课-3.1.3香甜的黄油

宣传一下算法提高课整理 <— CSDN个人主页&#xff1a;更好的阅读体验 <— 题目传送门点这里 题目描述 农夫John发现了做出全威斯康辛州最甜的黄油的方法&#xff1a;糖。 把糖放在一片牧场上&#xff0c;他知道 N 只奶牛会过来舔它&#xff0c;这样就能做出能卖好价…...

私库搭建1:Nexus 安装 Docker 版

本文内容以语雀为准 文档 https://hub.docker.com/r/sonatype/nexus3Docker 安装&#xff1a;https://www.yuque.com/xuxiaowei-com-cn/gitlab-k8s/docker-install 安装 创建文件夹 由于 Nexus 的数据可能会很大&#xff0c;比如&#xff1a;作为 Docker、Maven 私库时&…...

LeetCode-面试题 05.02. 二进制数转字符串【数学,字符串,位运算】

LeetCode-面试题 05.02. 二进制数转字符串【数学&#xff0c;字符串&#xff0c;位运算】题目描述&#xff1a;解题思路一&#xff1a;简单暴力。小数点后面的二进制&#xff0c;now首先从0.5开始之和每次除以2。然后依次判断当前数是否大于now&#xff0c;是则答案加1。若等于…...

pandas: 三种算法实现递归分析Excel中各列相关性

目录 前言 目的 思路 代码实现 1. 循环遍历整个SDGs列&#xff0c;两两拿到数据 2. 调用pandas库函数直接进行分析 完整源码 运行效果 总结 前言 博主之前刚刚被学弟邀请参与了2023美赛&#xff0c;这也是第一次正式接触数学建模竞赛&#xff0c;现在已经提交等待结果…...

做网站需要多少钱呢/迅雷bt磁力链 最好用的搜索引擎

前端应用在不断壮大的过程中&#xff0c;内部模块间的依赖可能也会随之越来越复杂&#xff0c;模块间的 低复用性 导致应用 难以维护&#xff0c;不过我们可以借助计算机领域的一些优秀的编程理念来一定程度上解决这些问题&#xff0c;接下来要讲述的 IoC 就是其中之一。什么是…...

php网站建设案例教程/河南网站优化公司哪家好

自动关联包含两种机制&#xff1a; 一种是loadrunner通过对比录制和回放时服务器响应的不同&#xff0c;而提示用户是否进行关联&#xff0c;用户可自己创建关联规则&#xff0c;这个功能可以方便的使我们获得需要关联的部分&#xff0c;但同时也存在一定的问题&#xff0c;如…...

wordpress 美术馆/大型网站建设平台

模板介绍 精美PPT模板设计&#xff0c;简约创意个人简历求职竞聘PPT模板。一套岗位晋升幻灯片模板&#xff0c;内含橙色多种配色&#xff0c;精美风格设计&#xff0c;动态播放效果&#xff0c;精美实用。 一份设计精美的PPT模板&#xff0c;可以让你在汇报演讲时脱颖而出。 …...

域名查询是否被注册/郑州本地seo顾问

这个问题是由于使用了较新的C17标准语言&#xff0c;因为Windows旧的SDK定义有一个byte的类型&#xff0c;但在C17里也有定义std::byte类型&#xff0c;这样就会造成重复定义。 解决方法&#xff1a; 1.可以预定义一个宏&#xff1a;_HAS_STD_BYTE0&#xff0c;这样设置就可以…...

wordpress控件图标/百度 营销推广怎么操作

文章讲的是未来最赚钱的行业三甲&#xff0c;必须必是它们&#xff0c;随着云计算、大数据的发展&#xff0c;人工智能的研发也已经越来越精进&#xff0c;虽然人工智能尚处在初始阶段&#xff0c;但不可否认这个领域存在着大量的商机。 商机大的地方&#xff0c;一定少不了各大…...

做标识的网站 知乎/seo检测

对 Linux 新手非常有用的 20 个命令 http://www.oschina.net/translate/useful-linux-commands-for-newbies 对 Linux 中级用户非常有用的 20 个命令 http://www.oschina.net/translate/20-advanced-commands-for-linux-experts 对 Linux 专家非常有用的 20 个命令 http://www.…...