esp和ebp详解

2023-11-10

我的理解:

国外一个比较好的汇编网站

http://www.tenouk.com/Bufferoverflowc/Bufferoverflow1b.html

///

http://blog.sina.com.cn/s/blog_c3bab4650101ogfd.html


汇编指令ebp与esp的关系与作用
可以看到,初始情况下,ebp此时值为0012FEDC,也就是栈帧的地址,而栈顶地址esp值为0012FDFC。可以看到两个值有一定的关系。而帧指针的地址较高。

     然后我们让它执行前两句,push ebp,mov ebp,esp

汇编指令ebp与esp的关系与作用

可以看到前两句已经执行了,那么ebp跟esp的值也发生了变化。esp=0012FDF8,ebp=0012FDF8。为神马?一句句解读,push ebp,向栈里面压入了一个东西,那么栈顶此时应该发生变化了,也就是地址-4字节。为什吗是减法呢?因为是向低地址增长的,这点一定得注意。所以此时esp变化成了0012FDFC-4=OO12FDF8.至于ebp也等于0012FDF8就不解释了

汇编指令ebp与esp的关系与作用

此时呢,观察现在的值。栈顶esp=0012FDF4,而ebp=0012FDF8;没啥好说的,此时的栈顶已经又跑上去了,说明又有元素压栈了。那么执 行这句mov esp,ebp之后,不用说,esp跟ebp都会变成0012FDF8.我们重点看下一幅,执行完pop,让ebp出栈,后会发生神马。

汇编指令ebp与esp的关系与作用

此时ebp已经出栈了,来看看那他们的值,esp=0012FDFC,ebp=0012FEDC.首先,ebp出栈了,这个时候栈空了,所以栈顶会变成初始时的值001212FDFC。相当于上图中的esp=0012FDF8+4=0012FDFC.注意出栈,则栈顶+4,然后呢。ebp为啥变成了0012FEDC初始的值?ebp不是一直保存着esp的初始地址么?       所以重点就在pop这个语句了。pop ebp究竟表达神马意思?ebp的值起初存在了栈中,出栈以后,它的值就恢复了原样。所一句灰常重要啊汇编指令ebp与esp的关系与作用。pop的意思也许就是把弹出的值赋给我们的变量,pop  ebp,也就是把存在栈中的值弹出来赋给ebp。

所以我们在啰唆几句:

1、两句的mov ebp,esp实际上是把ebp进栈后的栈顶地址给了ebp。

2、在ebp没有出栈钱,它会一直保存ebp进栈以后的栈顶值,也就是1的值。

3、在ebp出栈前,需要把esp恢复到只有ebp在栈中时的值。

4、出栈后,esp自然恢复到ebp进栈以前的初始值,而pop ebp则恢复了ebp的初始值。

5、pop的语义很重要,pop  ebp的意思是把当前栈顶的元素出栈,送入ebp中,而不是让ebp出栈,这点必须明确!汇编指令ebp与esp的关系与作用

       说完这个以后,我也有了疑问,因为从上面的解释来看,似乎栈里面的东西没有清空?就直接把esp恢复到了ebp的值,这是为神马呢?发现这就是所谓的栈平 衡问题,先不纠结了,只要有前两句push ebp, mov ebp,esp跟最后两句mov esp,ebp,pop  ebp就够了,这样肯定不会有问题。

      搞点学习笔记,比较下cmp跟test的指令,这两个都是比较检测指令。

     cmp  eax,xxxx,意思是让两者相减,但是并不保存运算的结果,只用来影响相应的标志位,从而判断是否执行跳转,比如说一下的例子是很常见的:

   cmp eax,1

   JE xxxx

      JE的跳转条件是:ZF=0,也就是当两者相等的时候,执行跳转。那么这个命令组合起来就是表示:如果eax=1,执行跳转

   顺便 再说下JNE,JNE的意思是jump if not eaqual,也就是跟JE的判断条件相反

       然后说下TEST吧,test eax,xxx:意思是让eax跟xxx执行与运算,同样也是不保存运算结果,而影响相应的标志位,TEST呢经常用来干下面这件事情.

    test eax,eax

    JZ    xxxx

    意思很明显,就是检测EAX的值是不是0,所以这个语句就是检测判断语句。JZ嘛就是jump if zero。组合起来也就是表示如果eax为0,执行跳转到xxxx。

      觉得还是CALL指令最难理解,它先把IP压栈,然后跳到子程序里面去执行东西,最后IP出栈,指令返回到先前执行的地方。继续OD吧。

 __________________________________________________________________________________________________________

       经过了激励的思想斗争,我终于想清楚了这个过程。现在可以毫无压力的,淡定的说,ebp保存的是栈帧起始的其实地址,也就是一个函数属于自己的内存那一块 的最高地址,而esp就保存了当前的这一块地址中已经使用的最低地址,说起来可能有些拗口。但是这对于深刻理解这个过程是至关重要的。

       有一句话让我想明白了这个道理,ebp总是跟旧的栈帧的顶部相邻。这样我们就可以理解为什么,调用一个函数的时候需要push ebp,那就保存旧的栈帧,同时也给出了我们得到旧的ebp的方法,一旦当前的栈帧使用完毕以后,这个最后弹出来的ebp就指明了我们怎么样回到旧的函数 的栈帧,或者知道那个调用我们的上一层函数的栈帧。mov ebp,esp这句话很有迷惑性。其实很好理解,这个告诉了我们新的栈帧是怎么创建的,其实就是直接的拿当前函数(ebp已经入栈后)的栈顶当做新的栈帧 头部,因为此时已经有esp=ebp了,而旧的ebp被我们保存在了栈里面。

      关于这个布局,CSAPP说的很清楚,深刻的理解了以上两句的作用,很多事情都能顺理成章的想通了。

       其实说白了,ebp只有一个作用,就是用来暂时保存当前函数栈的起始地址,因为旧的栈帧跟新的栈帧是相邻的,ebp相当于起一个咋暗示的temp的作用。

转自:http://hi.baidu.com/anheizzq/item/1c0899622926c81e7ddecca3


///


http://blog.csdn.net/running_noodle/article/details/2838679

= 问题 = 
  pushl %ebp 
movl %esp,%ebp 
干嘛要这样?  
 

--------------------------------------------------------------------------------
  esp是堆栈指针  
ebp是基址指针 

那两条指令的意思是 将栈顶指向 ebp 的地址  
--------------------------------------------------------------- 

楼主写的好像是要用GCC来编译的汇编? 

以下摘自我的一篇文章: 

push    ebp             ;ebp入栈  
mov     ebp, esp        ;因为esp是堆栈指针,无法暂借使用,所以得用ebp来存取堆栈  
sub     esp, 4*5        ;下面的wsprintf一共使用了5个参数,每个参数占用4个字节,所以要入栈4*5个字节  
push    1111  
push    2222  
push    3333  
push    offset szFormat  
push    offset szOut  
call    wsprintf        ;调用wsprintf  
add     esp, 4*5        ;堆栈使用完毕,“还”回4*5个字节给系统  
...  
mov     esp, ebp        ;恢复esp的值  
pop     ebp             ;ebp出栈  
ret 


明白了吗?主要是用来保存/恢复堆栈,以便传递参数给函数。  
在MASM里面,有一条更方便的语句,就是invoke  
使用它后,你就不用自己做这些事情了。 


--------------------------------------------------------------- 

esp始终指向栈顶,ebp是在堆栈中寻址用的 

 

************************************************************


调用一个函数时,先将堆栈原先的基址(EBP)入栈,以保存之前任务的信息。然后将栈顶指针的值赋给EBP,将之前的栈顶作为新的基址(栈底),然后再这个基址上开辟相应的空间用作被调用函数的堆栈。函数返回后,从EBP中可取出之前的ESP值,使栈顶恢复函数调用前的位置;再从恢复后的栈顶可弹出之前的EBP值,因为这个值在函数调用前一步被压入堆栈。这样,EBP和ESP就都恢复了调用前的位置,堆栈恢复函数调用前的状态。


///

http://www.cnblogs.com/dormant/p/5059644.html

程序如下:

 这个程序很简单,就是求两个数的值,然后输出即可。所以首先把它用gcc编译链接成a.out,进入gdb进行调试。

首先在main和add两处设置断点。运行到第一个断点,查看main的汇编代码:

我们主要是观察调用add后我们的esp和ebp的变化,于是输入命令:c,继续运行到add处,观察add的汇编:

其实也就是运行到了main的call指令处进入add函数了。这时到了观察esp和ebp的时刻了。

上图其实已经很明显了。我们来看下面的图,这样立体观察(栈帧结构图)就会瞬间明白了:

我把栈帧的每个字节都拆开来看就会有下面的结论,通过GDB,查找出这时esp和ebp的值,均为0xbffff0a8。

详解如下:

我们知道调用call指令后会有下面的三件事:

  • call指令保存返回地址所谓保存返回地址(return address)其实就是 call指令将那一时刻的PC(%eip值,即call的下一条指令的地址)压入栈(如上图中的0x8048447)。还记得吗?因为PC自增在先,指令执行在后。所以执行完add的所有代码后,ret指令会恢复PC的值,程序就可以继续执行main的剩余代码了。
  • add()保存main()的%ebp将add()栈帧的base地址压入到栈上。

这时查看从0xbffff0a8开始每个字节里的内容,从上面看出从0xbffff0a8开始向上的16个字节的内容(我把其中的8个放到了栈帧结构图中了)。main栈帧的最后4个字节代表着返回地址,通过gdb的查看也可以明显看出里边的内容就是main中call指令的下一条指令的地址。当我们读取0xbffff08a中的内容时(按双字节读)结果显示0xbffff0d8.可见这里是小端存储,读取是从高地址开始读取,其值表示main中的基址。这里有一个误区就是:我们在数据结构中学的栈顶指针总是指向栈顶元素的下一个位置,但在这里确实指向栈顶元素。这里千万要注意。


/

http://www.cnblogs.com/this-543273659/archive/2012/03/04/2379587.html

压栈一次esp-4,ebp不变

 

esp是栈顶指针寄存器,堆栈操作只和esp有关
比如有一个函数a,有两个参数,一般是这样的
PUSH 1 参数2压栈,esp-4
PUSH 2 参数1压栈,esp-4
CALL a 调用

 

a:
PUSH EBP 保存ebp
MOV EBP,ESP 改变栈帧,以后访问参数通过ebp,访问局部变量通过esp
SUB ESP,8 分配局部变量空间

...
ADD ESP,8
POP EBP 恢复ebp
RETN 8 返回,esp+8

 

C语句对应汇编语句:

例如函数:

int aaa(int a,int b)
{
int c;
c=a+b;
return c;
}

aaa(1,2);

 

调试版aaa的代码
PUSH EBP
MOV EBP,ESP
SUB ESP,4//分配局部变量空间,一个int是4个字节
MOV EAX,DWORD PTR SS:[EBP+8]//读取参数a
ADD EAX,DWORD PTR SS:[EBP+C]//加上参数b
MOV DWORD PTR SS:[EBP-4],EAX//保存到局部变量c
MOV EAX,DWORD PTR SS:[EBP-4]//eax是返回值
MOV ESP,EBP//恢复栈顶指针
POP EBP//恢复ebp
RETN//返回

 

调用
PUSH 2//参数2压栈,esp-4
PUSH 1//参数1压栈,esp-4
CALL aaa//调用函数
ADD ESP,8//esp+8,平衡堆栈,清除掉参数

发布版的就是这样了,精简掉了很多内容
MOV EAX,DWORD PTR SS:[ESP+8]
MOV ECX,DWORD PTR SS:[ESP+4]
ADD EAX,ECX
RETN

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

 

下面要讲的是子程序如何存取参数,因为缺省对堆栈操作的寄存器有 ESP 和 EBP,而 ESP是堆栈指针,无法暂借使用,所以一般使用 EBP 来存取堆栈,假定在一个调用中有两个参数,而且在 push 第一个参数前的堆栈指针 ESP 为 X,那么压入两个参数后的 ESP 为 X-8,程序开始执行 call 指令,call 指令把返回地址压入堆栈,这时候 ESP 为 X-C,这时已经在子程序中了,我们可以开始使用 EBP 来存取参数了,但为了在返回时恢复 EBP 的值,我们还是再需要一句 push ebp 来先保存 EBP 的值,这时 ESP 为 X-10,再执行一句 mov ebp,esp,根据上图可以看出,实际上这时候 [ebp + 8] 就是参数1,[ebp + c]就是参数2。另外,局部变量也是定义在堆栈中的,它们的位置一般放在 push ebp 保存的 EBP 数值的后面,局部变量1、2对应的地址分别是 [ebp-4]、[ebp-8],下面是一个典型的子程序,可以完成第一个参数减去第二个参数,它的定义是:

 

   MyProc proto Var1,Var2 ;有两个参数
   local lVar1,lVar2 ;有两个局部变量

 

   注意,这里的两个 local 变量实际上没有被用到,只是为了演示用,具体实现的代码是:

 

MyProc proc

 

   push ebp
   mov ebp,esp
   sub esp,8
   mov eax,dword ptr [ebp + 8]
   sub eax,dword ptr [ebp + c]
   add esp,8
   pop ebp
   ret 8

 

MyProc endp

 

   现在对这个子程序分析一下,push ebp/mov ebp,esp 是例行的保存和设置 EBP 的代码,sub esp,8 在堆栈中留出两个局部变量的空间,mov /add 语句完成相加,add esp,8 修正两个局部变量使用的堆栈,ret 8 修正两个参数使用的堆栈,相当于 ret / add esp,8 两句代码的效果。可以看出,这是一个标准的 Stdcall 约定的子程序,使用时最后一个参数先入堆栈,返回时由子程序进行堆栈修正。当然,这个子程序为了演示执行过程,使用了手工保存 ebp 并设置局部变量的方法,实际上,386 处理器有两条专用的指令是完成这个功能用的,那就是 Enter 和 Leave,Enter 语句的作用就是 push ebp/mov ebp,esp/sub esp,xxx,这个 xxx 就是 Enter 的,Leave 则完成 add esp,xxx/pop ebp 的功能,所以上面的程序可以改成:

 

MyPorc proc

 

   enter 8,0

 

   mov eax,dword ptr [ebp + 8]
   sub eax,dword ptr [ebp + c]

 

   leave
   ret 8

 

MyProc endp

 

文章出处:飞诺网(www.diybl.com):http://www.diybl.com/course/3_program/hb/hbjs/20071226/93663_2.html

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

一:在分析汇编代码时总是要遇到无数的 Call ,对于这些 Call ,尽量要根据 Call 之前传递的参数和 Call 的返回值来判断 Call 的功能。传递参数的工作必须由函数调用者和函数本身来协调,计算机提供了一种被称为栈的数据结构来支持参数传递。 
    当参数个数多于一个时,按照什么顺序把参数压入堆栈。函数调用后,由谁来把堆栈恢复。在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有:

二:堆栈框架也称为活动记录,它为程序的返回地址,传递进来的参数,保存的寄存器喝局部变量保存的堆栈空间。堆栈框架是按以下的步骤创建的。

1 :参数被压入堆栈。

2 :过程被调用,返回地址被压入堆栈。

3 :过程开始执行时, EBP 被压入堆栈。

4 :使 EBP 和 ESP 的值相等,从这里开始, EBP 就作为寻址参数的基址指针。

5 :可以从 ESP 中减掉一个数值来给过程的局部变量创建空间。

 

【例】按 __stdcall 约定调用函数 test2(Par1, Par2)   


        push par2  ;  参数 2              ; 参数被压入堆栈(从右向左) 
        push par1  ;  参数 1              ;
        call test2;                            ; 过程被调用 
        {                                ; 过程开始执行 
             push ebp                    ; EBP 被压入堆栈,保护现场原先的 EBP 指针 
             mov  ebp, esp            ; 使 EBP=ESP,  设置新的 EBP 指针,指向栈顶,

; 从这里开始, EBP 就作为寻址参数的基址指针。 
             mov  eax, [ebp+0C]  ;  调用参数 2
             mov  ebx, [ebp+08]   ;  调用参数 1
             sub  esp, 8              ;  若函数要用局部变量,则要在堆栈中留出点空间

; 从 ESP 中减掉一个数值来给过程的局部变量创建空间 
             …
             add  esp, 8                 ;  释放局部变量占用的堆栈 
             pop  ebp                    ;  恢复现场的 ebp 指针 
             ret  8                      ;  返回(相当于 ret; add esp,8 ) 
        }

三 : 其堆栈调用示意图:(编译原理的教程中说的更清楚)

 

 

四 : 例子

 

 

00401000  /$ 6A04        push    4               ; /Arg2 = 00000004

00401002  |. 6A03         push    3                ; |Arg1 = 00000003

00401004  |.  E8 16000000   call   0040101F          ; \local.0040101F

00401009  |.  8BD8         mov     ebx, eax

0040100B  |. 6A00         push    0                ; /ExitCode = 0

0040100D  \.  FF15 00204000 call    dword ptr [<&KERNEL32.ExitProces>; \ExitProcess

 

 

 

 

 

00401013      00            db      00

00401014      00            db      00

00401015      00            db      00

00401016      00            db      00

00401017      00            db      00

00401018      00            db      00

00401019      00            db      00

0040101A      00            db      00

0040101B      00            db      00

0040101C      00            db      00

0040101D      00            db      00

0040101E      00            db      00

 

 

 

 

 

 

0040101F  /$  55           push    ebp

00401020  |.  8BEC         mov    ebp, esp

; 使 EBP=ESP=0012FFB4

; 设置新的 EBP 指针,指向栈顶,

; 从这里开始, EBP 就作为寻址参数的基址指针。

00401022  |.  83EC 04       sub     esp, 4

; 扩展栈空间 ESP=0012FFB0

00401025  |.  8B450C       mov    eax, dword ptr [ebp+C] ;

                                                               ; 当前堆栈 3 个双字偏移的数值

                                                               ; ebp+C=0012FFB4+C=0012FFC0

                                                               ;EAX=[0012FFC0]=00000004

00401028  |.  8B5D 08       mov     ebx, dword ptr [ebp+8]

                                                               ; 当前堆栈 2 个双字偏移的数值

                                                               ; ebp+8==0012FFB4+8=0012FFBC

                                                               ;EBX=[0012FFBC]=00000003

0040102B  |.  895D FC      mov     dword ptr [ebp-4], ebx

                                                               ;[ebp-4] 就是局部变量 =[0012FFB4-4]=[0012FFB0]

                                                               ; 自动减 4, 也就是将 EBX 中的值 00000003

; 放入堆栈的 0012FFB0 地址处

; 参数 1 放局部变量里

0040102E  |.  0345 FC       add     eax, dword ptr [ebp-4]

                                                               ; 将局部变量与 EAX 相加后放入 EAX 中

                                                               ;EAX=00000007

                                                               ; 参数 2 与局部变量相加

00401031  |. 83C4 04       add     esp, 4

                                                               ;  释放局部变量占用的堆栈 ,ESP: 0012FFB4

00401034  |.  5D            pop     ebp

                                                               ; 恢复原有的 EBP, ESP:0012FFB8

00401035  \.  C2 0800       retn    8

                                                               ;  返回 ( 相当于 ret; add esp,8)

                                                               ; ESP: 0012FFC4

 

 

 

堆栈的情况:

0012FFB0   00000003        ; 局部变量

0012FFB4   0012FFF0              ; 保存 EBP , push    ebp

0012FFB8   00401009       ; 压入返回地址

; 返回到 local.< 模块入口点 >+9 来自 local.0040101F

0012FFBC   00000003        ; push    3 ,每次堆栈地址加 32 位,双字

0012FFC0   00000004        ; push    4

 

 

 

 

五 : 说明

Intel 的堆栈是在内存中是向下扩展的。先进栈的数据内存地址最高,后进栈的数据内存地址减少。且数据是按小尾类型存储,例如:数值 12345678H 存放的形式(假设按字):先存 1234 ,后存放 5678



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

esp和ebp详解 的相关文章

随机推荐

  • 编程常用快捷键和doc命令

    常用快捷键 win r 打开运行 cmd命令行窗口 win e打开我的电脑 ctrl shift esc打开任务管理器 doc命令 打开doc win r 输入cmd shift右键任意文件夹选择在此处运行powershell窗口 在资源管
  • 【网站数据统计解决方案】快速了解pv、uv、spm、utm_source、埋点等知识

    前言 在访问阿里网站或者一些博客网站的时候 发现地址后面跟了 spm 1010 2135 3001 4477这种参数 以及在访问国外网站的时候会跟 utm source google utm medium cpc utm campaign
  • 番茄ToDo帮助文档

    说明 2020年7月开始使用 番茄ToDo App 目标是通过手机App这样的工具提升效率 养成习惯 提升自我管理能力 番茄ToDo帮助文档 什么是番茄ToDo 番茄工作法是简单易行的时间管理方法 是由弗朗西斯科 西里洛于1992年创立的一
  • 如何去除discuz的powered by discuz!代码

    这串代码 很多人都在问这个问题 今天在这里分享一下 方法 步骤 首选在FTP里面找到源文件夹template default common header common htm 右击编辑 2 打开后找到里面的这串代码 3 将后面的 Power
  • 创建和分析二维桁架和梁结构研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码及讲解 1 概述 创建和分析二维桁架和梁结构的研究可以涉及
  • python 实现二叉树

    本文不涉及二叉树概念的详细讲解 而着重利用 python 实现二叉树 其中穿插代码讲解 其它数据结构 链表 栈和队列 目录 节点 构造树 层遍历 添加节点 先序遍历 中序遍历 后序遍历 测试 在链表中 一个节点只能有一个后继节点和前驱节点
  • find grep 寻找文件字符

    如果你只想在 r 类型的文件中寻找特定字符串 可以使用以下命令 grep ri universal include r i 忽略大小写 r递归所有文件 如果你只想查找文件名包含 universal 且扩展名为 r 的文件 而不是文件内容 则
  • torch.cuda.is_available()为false的解决办法

    一 问题 在进行torch进行开发的过程中 我们习惯性的会使用pip install torch这样的方式来安装torch的包 其实这样的是安装CPU的torch 在导入包 执行下面代码的过程中 会出现结果为false import tor
  • win10通过conda安装pytorch gpu

    1 安装anaconda 到官网下载最新版的anaconda 下载对应的windows版本 地址 anaconda官网 下载后直接安装 安装完成后配置环境变量 具体可以百度anaconda安装说明 安装完成后 打开cmd 输入conda v
  • Nginx HTTPS实践

    Nginx HTTPS实践 文章目录 Nginx HTTPS实践 1 HTTPS基本概述 1 1 为何需要HTTPS 1 2 什么是HTTPS 1 3 TLS如何实现加密 2 HTTPS实现原理 2 1 加密模型 对称加密 2 2 加密模型
  • 用matlab编程实现对图像的均值滤波,中值滤波和拉普拉斯算子锐化

    1 均值滤波 均值滤波 用包含在滤波掩模邻域内的像素的平均灰度值去代替每个像素点的值 用途 用于模糊处理和减少噪声 盒滤波器 加权平均滤波器 均值滤波 clc close all clear all I rgb2gray imread fi
  • java编程练习

    package HomeWork05 import java util Scanner public class HomeWork05 public static void main String args Scanner sc new S
  • PAMI19 - 强大的级联RCNN架构《Cascade R-CNN: High Quality Object Detection and Instance Segmentation》

    文章目录 原文 初识 相知 Challenge to High Quality Detection Cascade RCNN 与相似工作的异同 扩展到实例分割 回顾 参考 原文 https arxiv org pdf 1906 09756
  • springboot框架中使用websocket传输内容过长的问题解决

    很多业务中使用websocket进行前后台的长连接 一般情况下用作及时性消息推送等 而一旦传输内容过长 例如传输一些图片音频的base64编码之类的 很容易出现过长问题 甚至不提示问题直接截断乃至丢失数据 解决方法如下 很多人网上查阅方法会
  • 【算法 -- LeetCode】(026)删除有序数组中的重复项

    1 题目 给你一个 升序排列 的数组 nums 请你 原地 删除重复出现的元素 使每个元素 只出现一次 返回删除后数组的新长度 元素的 相对顺序 应该保持 一致 然后返回 nums 中唯一元素的个数 考虑 nums 的唯一元素的数量为 k
  • CORDIC算法详解及FPGA实现

    CORDIC算法详解 1 平面坐标系旋转 CORDIC算法的思想是通过迭代的方法 使得累计旋转过的角度的和无限接近目标角度 它是一种数值计算逼近的方法 运算只有移位和加减 通过圆坐标系可以了解CORDIC算法的基本思想 如图1所示 初始向量
  • 线边仓

    SAP线边库管理 是又叫WIP仓 或叫线上仓 举例来说 一卷线材 总长303米 工单需用100米 于是发料发出303米 也就是一卷 其中100米上生产线 另外203米进入这个WIP仓 下次领料直接从WIP仓发出去 管控非常到位 具体操作就要
  • 11月20日 作业3 生命药剂,角色蓝图修改

    具体太麻烦了 我直接贴代码了 SPowerUp Actor h Fill out your copyright notice in the Description page of Project Settings pragma once i
  • 录屏没有声音?录制声音,3招教你搞定

    在录制屏幕内容时 声音是不可或缺的要素之一 可以有效地增强录制视频的表现力和传达效果 然而 有时候可能会遇到录屏没有声音的情况 这可能会让录制的视频失去一部分重要信息 本文将为您介绍录屏录声音的3种方法 帮助您解决录屏没有声音的问题 实现高
  • esp和ebp详解

    我的理解 国外一个比较好的汇编网站 http www tenouk com Bufferoverflowc Bufferoverflow1b html http blog sina com cn s blog c3bab4650101ogf