x86 汇编手册快速入门

本文翻译自:Guide to x86 Assembly

在阅读 Linux 源码之前,我们需要有一些 x86 汇编知识。本指南描述了 32 位 x86 汇编语言编程的基础知识,包括寄存器结构,数据表示,基本的操作指令(包括数据传送指令、逻辑计算指令、算数运算指令),以及函数的调用规则。

一、寄存器(Registers)

如下图,现代 x86 处理器有 8 个 32-bit 的通用寄存器: 

由于历史原因,EAX 寄存器过去被用于算术运算,ECX被用于保存循环索引。尽管现在大多数寄存器在现代指令集中已经失去了它们的特殊用途,但有两个寄存器一直保留用于特殊用途——堆栈指针(ESP,用于指示栈顶位置)和基址指针(EBP,用于指示子程序或函数调用的基址指针)。

对于 EAXEBXECXEDX 四个寄存器来说,其前两个高位字节和后两个低位字节可以独立使用,而后两个低位字节又分为高位(H)和低位(L)部分。这样做的原因主要是为了兼容 16 位程序。

二、内存和寻址模式(Memory and Addressing Modes)

2.1 声明静态数据区(Declaring Static Data Regions)

我们可以使用特殊的汇编指令 .DATA 在 x86 汇编中声明静态数据区域(类似于全局变量),在此指令之后,可以使用指令 DBDW DD分别声明一个、两个和四个字节的数据大小,声明的数据可以用标签(label)标记,以供以后引用。并且按顺序声明的数据将位于内存中相邻的位置

下面是一个相关的例子:

.DATA			
var    DB 64       ; 声明一个字节, 里面的值为 64
var2   DB ?	       ; 声明一个未初始化的字节DB 10       ; 声明一个没有 label 的字节, 其值为 10
X	   DW ?	       ; 声明一个未初始化的双字节
Y	   DD 30000    ; 声明一个四字节,值为 30000

x86 汇编语言中的数组只是在内存中连续放置的若干单元格,可以使用 DUP 指令来声明数据数组:


Z     DD 1, 2, 3	; 声明三个四字节的值, 分别初始化为 1, 2, 3; Z+8 位置存放的是 3
bytes DB 10 DUP(?)	; 声明 10 个未初始化的单字节数据
arr	  DD 100 DUP(0) ; 声明 100 个初始化为 0 的四字节数据
str	  DB 'hello',0	; 声明 6 个单字节数据,前五个被初始化为 ASCII 字符, 最后一个初始化为 null(0) 字节

2.2 内存寻址(Addressing Memory)

现代 x86 处理器最多能够寻址  2^{32} 字节大小的内存。在上面的例子中,我们使用标签表示内存区域,这些标签在实际汇编时,均被 32 位的实际地址代替。除了支持这种直接的内存区域描述,X86 还提供了一种灵活的内存寻址方式,即利用最多两个 32 位的寄存器和一个 32 位的有符号常数相加计算一个内存地址,其中一个寄存器可以乘 2, 4 或 8 以表述更大的空间。

下面是正确使用的例子:

mov eax, [ebx]	     ; 将 ebx 值指示的内存地址中的 4 个字节移动到 eax 中
mov [var], ebx	     ; 将 ebx 的内容移动到 var 标签所指示的内存地址中
mov eax, [esi-4]	 ; 将 esi-4 值指示的内存地址中的 4 个字节移动到 eax 中
mov [esi+eax], cl	 ; 将 cl 值的内容移动到 esi+eax 所指示的内存地址中
mov edx, [esi+4*ebx] ; 将 esi+4*ebx 所指示的内存地址中的 4 个字节移动到 edx 中

下面是错误使用的例子:

mov eax, [ebx-ecx]	    ; 只能将寄存器的值相加,不能相减
mov [eax+esi+edi], ebx  ; 最多只能有两个寄存器相加

通常,给定内存地址中数据项的预期大小可以从引用它的汇编代码指令推断出来。当我们加载一个 32 位寄存器时,汇编器可以推断我们引用的内存区域是 4 字节宽。当我们将一个字节寄存器的值存储到内存中时,汇编器可以推断出我们希望地址指向内存中的一个字节。

但对于 mov [ebx], 2 这条指令来说,汇编器无法判断应该将 2 值作为单个字节的数据还是多个字节的数据,这时就需要用到指令 BYTE PTRWORD PTRDWORD PTR,分别表示 1、2 和 4 字节的大小:

mov BYTE PTR [ebx], 2	; 将一个字节表示的 2 移动到 ebx 所指向的内存地址处
mov WORD PTR [ebx], 2	; 将两个字节表示的 2 移动到 ebx 所指向的内存地址处
mov DWORD PTR [ebx], 2  ; 将四个字节表示的 2 移动到 ebx 所指向的内存地址处

三、指令(Instructions) 

机器指令通常分为三类:数据移动指令、算术/逻辑指令和控制流指令。在本节中,我们将分别介绍每种类型的重要 x86 指令示例。 这会使用到如下的几种符号表示:

<reg32>    任意 32 位寄存器 (EAX, EBX, ECX, EDX, ESI, EDI, ESP, or EBP)
<reg16>任意 16 位寄存器 (AX, BX, CX, or DX)
<reg8>任意 8 位寄存器 (AH, BH, CH, DH, AL, BL, CL, or DL)
<reg>任意寄存器
<mem>内存地址 (e.g., [eax], [var + 4], or dword ptr [eax+ebx])
<con32>任意 32 位常数
<con16>任意 16 位常数
<con8>任意 8 位常数
<con>任意 32 位、16 位或 8 位常数

3.1 数据移动指令(Data Movement Instructions)

mov — Move (Opcodes: 88, 89, 8A, 8B, 8C, 8E, ...) 

mov指令将其第二个操作数内容(可以是寄存器、内存或常量)复制到其第一个操作数(寄存器或内存)中。 不能直接用 mov 指令将内存中的值移动到另外一个内存地址。下面是 mov 指令的语法:

mov <reg>,<reg>
mov <reg>,<mem>
mov <mem>,<reg>
mov <reg>,<const>
mov <mem>,<const>

例子:

mov eax, ebx           ; 复制 ebx 中的内容到 eax
mov byte ptr [var], 5  ; 将单字节表示的 5 存入 var 所指示的内存单元

push — Push stack (Opcodes: FF, 89, 8A, 8B, 8C, 8E, ...)

push 指令操作数压入内存的栈中。具体来说,push 指令首先将 ESP (栈指针) 减 4(因为 x86 栈向下增长),然后将其操作数放入地址为 [ESP] 位置的内存单元中。用法如下:

push <reg32>
push <mem>
push <con32>

例子:

push eax — push eax on the stack
push [var] — push the 4 bytes at address var onto the stack

pop — Pop stack

pop 指令从栈中取出一个数据放入指定寄存器或内存中。pop 指令首先移动地址为 [ESP] 位置的内存单元中的数据到指定寄存器或内存,然后再将 ESP 加 4。用法如下:

pop <reg32>
pop <mem>

例子:

pop edi — pop the top element of the stack into EDI.
pop [ebx] — pop the top element of the stack into memory at the four bytes starting at location EBX.

lea — Load effective address

lea 指令将第二个操作数指定的内存地址放置到第一个操作数指定的寄存器中。该指令不会加载该内存位置的内容,只计算有效地址并放入寄存器。用法如下:

lea <reg32>,<mem>

例子:

lea edi, [ebx+4*esi] — the quantity EBX+4*ESI is placed in EDI.
lea eax, [var] — the value in var is placed in EAX.
lea eax, [val] — the value val is placed in EAX.

3.2 算术/逻辑指令(Arithmetic and Logic Instructions)

add — Integer Addition

add 指令将两个操作数相加,结果存储到第一个操作数中。用法如下:

add <reg>,<reg>
add <reg>,<mem>
add <mem>,<reg>
add <reg>,<con>
add <mem>,<con>

例子:

add eax, 10 — EAX ← EAX + 10
add BYTE PTR [var], 10 — add 10 to the single byte stored at memory address var

sub — Integer Subtraction

sub 指令将两个操作数相减,结果存储到第一个操作数中。用法如下:

sub <reg>,<reg>
sub <reg>,<mem>
sub <mem>,<reg>
sub <reg>,<con>
sub <mem>,<con>

例子:

sub al, ah — AL ← AL - AH
sub eax, 216 — subtract 216 from the value stored in EAX

inc, dec — Increment, Decrement

inc 指令将操作数的内容加一,而 dec 指令将操作数的内容减一。用法如下:

inc <reg>
inc <mem>
dec <reg>
dec <mem>

例子:

dec eax — subtract one from the contents of EAX.
inc DWORD PTR [var] — add one to the 32-bit integer stored at location var

imul — Integer Multiplication

imul 指令分为两个操作数和三个操作数两种类型,如果是两个操作数,则让两个操作数相乘,并将结果存入第一个操作数中;如果是三个操作数,则让第二个和第三个操作数相乘,结果存入第一个操作数中,且第三个操作数必须是常数。不管哪种类型,第一个操作数必须是寄存器。用法如下:

imul <reg32>,<reg32>
imul <reg32>,<mem>
imul <reg32>,<reg32>,<con>
imul <reg32>,<mem>,<con>

例子:

imul eax, [var] — multiply the contents of EAX by the 32-bit contents of the memory location var. Store the result in EAX.
imul esi, edi, 25 — ESI → EDI * 25

idiv — Integer Division

idiv 指令将 64 位整数 EDX:EAX (将 EDX 视为高 4 字节,将 EAX 视为低 4 字节) 的内容除以指定的操作数值。除法的商存储在 EAX 中,余数存储在 EDX 中。 用法如下:

idiv ebx — divide the contents of EDX:EAX by the contents of EBX. Place the quotient in EAX and the remainder in EDX.
idiv DWORD PTR [var] — divide the contents of EDX:EAX by the 32-bit value stored at memory location var. Place the quotient in EAX and the remainder in EDX.

and, or, xor — Bitwise logical and, or and exclusive or

and,or,xor 指令分别对两个操作数作与、或、异或操作,结果存入第一个操作数中。用法如下:

and <reg>,<reg>
and <reg>,<mem>
and <mem>,<reg>
and <reg>,<con>
and <mem>,<con>or <reg>,<reg>
or <reg>,<mem>
or <mem>,<reg>
or <reg>,<con>
or <mem>,<con>xor <reg>,<reg>
xor <reg>,<mem>
xor <mem>,<reg>
xor <reg>,<con>
xor <mem>,<con>

例子:

and eax, 0fH — clear all but the last 4 bits of EAX.
xor edx, edx — set the contents of EDX to zero.

not — Bitwise Logical Not

not 指令逻辑上对操作数的内容求反 (即翻转操作数中的所有位)。用法如下:

not <reg>
not <mem>

neg — Negate

neg 指令对操作数内容取负。用法如下:

neg <reg>
neg <mem>Example
neg eax — EAX → - EAX

shl, shr — Shift Left, Shift Right

shl, shr 指令会左右移动第一个操作数的位,用0填充空的位。操作数最多可以被移动 31 位。移位的位数由第二个操作数指定,若操作数大于 32,则对其求模。 用法如下:

shl <reg>,<con8>
shl <mem>,<con8>
shl <reg>,<cl>
shl <mem>,<cl>shr <reg>,<con8>
shr <mem>,<con8>
shr <reg>,<cl>
shr <mem>,<cl>

3.3 控制流指令(Control Flow Instructions)

x86 处理器维持着一个指示当前执行指令的指令指针(IP),当一条指令执行后,此指针自动指向下一条指令。IP 寄存器不能直接操作,但是可以用控制流指令更新(CS和IP寄存器的作用及执行分析_cs ip_猪哥-嵌入式的博客-CSDN博客)。

一般用标签(label)指示程序中的地址,在 x86 汇编代码中,可以在任何指令前加入标签。如:

       mov esi, [ebp+8]
begin: xor ecx, ecxmov eax, [esi]

这种标签只是用于取代 32 位地址值的一种便利的表示方式。这样在其他代码中就可以使用该标签,而不是使用其对应具体的 32 位地址。

jmp — Jump

jmp 指令用于跳转到指定的 label 位置处执行。用法如下:

jmp <label>Example
jmp begin — Jump to the instruction labeled begin.

jcondition — Conditional Jump

jcondition 类的指令用于条件跳转(即满足条件才跳转)。这些条件存储在一个称为机器状态字 (machine status word) 的特殊寄存器中。一般会在执行下列指令之前先执行 cmp 指令对两个操作数进行比较。用法如下:

je <label> (jump when equal)
jne <label> (jump when not equal)
jz <label> (jump when last result was zero)
jg <label> (jump when greater than)
jge <label> (jump when greater than or equal to)
jl <label> (jump when less than)
jle <label> (jump when less than or equal to)Example
cmp eax, ebx
jle done; 如果 eax 小于 ebx 的内容,则跳转到标签 done 所指向的位置处指向,若大于则继续往下执行

cmp — Compare

cmp 指令比较两个操作数指定的值,然后设置适当的机器状态字的状态码。这条指令等同于减法指令,但是会舍弃减法的结果。用法如下:

cmp <reg>,<reg>
cmp <reg>,<mem>
cmp <mem>,<reg>
cmp <reg>,<con>Example
cmp DWORD PTR [var], 10
jeq loop

callret — Subroutine call and return

call 和 ret 指令用于实现子程序的调用和返回。

call 指令首先将当前代码执行位置压入栈中,然后无条件跳转到 label 操作数指定的位置。与简单的跳转指令不同,该指令会保存当前代码执行位置,以便子程序执行完后返回。

ret 指令实现子程序的返回。该指令首先从栈弹出 call 指令保存的代码执行位置,然后无条件返回到该位置继续执行。

call <label>
ret

四、调用约定(Calling Convention)

为了加强程序员之间的协作及简化程序开发进程,设定一个函数调用约定非常必要,函数调用约定规定了函数调用及返回的规则,只要遵照这种规则写的程序均可以正确执行,从而程序员不必关心诸如参数如何传递等问题;另一方面,在汇编语言中可以调用符合这种规则的高级语言所写的函数,从而将汇编语言程序与高级语言程序有机结合在一起。有了这个约定,我们就可以在汇编代码中调用 C 函数,或者在 C 代码中调用汇编实现的函数。

调用约定分为两个方面,及调用者约定和被调用者约定,如一个函数 A 调用一个函数 B,则 A 被称为调用者 (Caller),B 被称为被调用者 (Callee)。

下图显示一个调用过程中的内存中的栈布局: 

4.1 Caller Rules

调用者规则包括一系列约定:

  1. 在调用子程序之前,调用者应该保存一系列被设计为调用者应该保存的寄存器的值。调用者应该保存的寄存器有 EAX、ECX、EDX。由于被调用的子程序允许修改这些寄存器,如果调用者在子程序返回后还需要用到这些寄存器的值,则调用者必须将这些寄存器中的值压入堆栈(以便在子例程返回后可以恢复它们)。 
  2. 要将参数传递给子例程,需要在调用之前将它们压入栈。参数应该按倒序推送 (即最后一个参数先 push),因为栈是向下增长的。
  3. 使用 call 指令调用子程序。该指令将返回地址放在栈的顶部,并转到子程序执行(子程序的执行将按照被调用者的规则执行)。

在子例程返回之后,调用者可以期望在寄存器 EAX 中找到子程序的返回值。为了恢复调用子程序执行之前的状态,调用者应该执行以下操作:

  1. 清除栈中的参数。
  2. 将栈中保存的 EAX 值、ECX 值以及 EDX 值出栈,恢复 EAX、ECX、EDX 的值(当然,如果其它寄存器在调用之前需要保存,也需要完成类似入栈和出栈操作)。

例子:

push [var] ; Push last parameter first
push 216   ; Push the second parameter
push eax   ; Push first parameter lastcall _myFunc ; Call the function (assume C naming)add esp, 12

上述例子展示了一个调用者执行的操作,在调用 _myFunc 函数之前,先保存函数的三个传入参数 eax、216 和 var,然后使用 call 指令调用函数,返回后再将 esp(栈指针)的值加 12(三个参数,每个四字节),加 12 的目的是为了清空栈中的三个参数。

_myFunc 的返回值保存在 eax 中。若该函数会改变 ecx 和 edx 中的值,调用者还必须在调用之前将其也保存在栈中,并在调用结束之后,出栈恢复ecx和edx的值。

4.2 Callee Rules

  1. 将 EBP(基址寄存器)入栈(push ebp),并将 ESP 中的值拷贝到 EBP 中(mov ebp esp),目的是保存调用子程序之前的基址指针,基址指针用于寻找栈上的参数和局部变量。当一个子程序开始执行时,基址指针保存栈指针指示子程序的执行。为了在子程序完成之后调用者能正确定位调用者的参数和局部变量,ebp的值需要返回。
  2. 在栈上为局部变量分配空间。
  3. 保存 callee-saved 寄存器的值,callee-saved 寄存器包括 ebx,edi 和 esi,将 ebx,edi 和 esi 压栈。

在上述三个步骤完成之后,子程序开始执行,当子程序返回时,必须完成如下工作:

  1. 将返回的执行结果保存在 eax 中
  2. 弹出栈中保存的 callee-saved 寄存器值,恢复 callee-saved 寄存器的值(ESI和EDI)
  3. 收回局部变量的内存空间。实际处理时,通过改变 EBP 的值即可:mov esp, ebp。 
  4. 通过弹出栈中保存的 ebp 值恢复调用者的基址寄存器值。
  5. 执行 ret 指令返回到调用者程序。

例子:

.486
.MODEL FLAT
.CODE
PUBLIC _myFunc
_myFunc PROC; Subroutine Prologuepush ebp     ; Save the old base pointer value.mov ebp, esp ; Set the new base pointer value.sub esp, 4   ; Make room for one 4-byte local variable.push edi     ; Save the values of registers that the functionpush esi     ; will modify. This function uses EDI and ESI.; (no need to save EBX, EBP, or ESP); Subroutine Bodymov eax, [ebp+8]   ; Move value of parameter 1 into EAXmov esi, [ebp+12]  ; Move value of parameter 2 into ESImov edi, [ebp+16]  ; Move value of parameter 3 into EDImov [ebp-4], edi   ; Move EDI into the local variableadd [ebp-4], esi   ; Add ESI into the local variableadd eax, [ebp-4]   ; Add the contents of the local variable; into EAX (final result); Subroutine Epilogue pop esi      ; Recover register valuespop  edimov esp, ebp ; Deallocate local variablespop ebp ; Restore the caller's base pointer valueret
_myFunc ENDP
END

子程序首先通过入栈的手段保存 ebp,分配局部变量,保存寄存器的值。

在子程序体中,参数和局部变量均是通过 ebp 进行计算。由于参数传递在子程序被调用之前,所以参数总是在 ebp 指示的地址的下方(在栈中),因此,上例中的第一个参数的地址是 ebp+8,第二个参数的地址是 ebp+12,第三个参数的地址是 ebp+16;而局部变量在 ebp 指示的地址的上方,所有第一个局部变量的地址是 ebp-4,而第二个这是 ebp-8.

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【leetcode 力扣刷题】数学题之数的开根号:二分查找

用二分查找牛顿迭代解决开根号 69. x的平方根367. 有效的完全平方数 69. x的平方根 题目链接&#xff1a;69. x的平方根 题目内容&#xff1a; 题意是要我们求一个数的算数平方根&#xff0c;但是不能使用内置函数&#xff0c;那么我们就暴力枚举。我们知道如果y>2的话&am…

PO设计模式是selenium自动化测试中最佳的设计模式之一

Page Object Model&#xff1a;PO设计模式是selenium自动化测试中最佳的设计模式之一&#xff0c;主要体现在对界面交互细节的封装&#xff0c;也就是在实际测试中只关注业务流程就OK了传统的设计中&#xff0c;在新增测试用例之后&#xff0c;代码会有以下几个问题&#xff1a…

经管博士科研基础【16】一元二次函数的解的公式

1. 一元二次函数的形式 2. 一元二次函数的图形与性质 一元二次函数的图像是一条抛物线&#xff0c;图像定点公式为(-b/2a,4ac-b*b/4a)&#xff0c;对称轴位直线x-b/2a。 3. 求根公式 形如ax*xb*xc0的一元二次方程&#xff0c;其求根公式为&#xff1a; 4. 韦达定理 如果x1和…

ScreenToGif-动图制作软件实用操作

ScreenToGif官网&#xff1a;ScreenToGif ⭕第一步&#xff1a;启动页面 ⭕第二步&#xff1a;选项 &#x1f95d;录像机-捕获频率选择手动-播放延迟1000ms(可以任意) ⭕第三步&#xff1a;录像机开始录屏 &#x1f95d;我们调整录屏的大小后&#xff0c;打开画图&#xff0c…

剪枝基础与实战(5): 剪枝代码详解

对模型进行剪枝,我们只对有参数的层进行剪枝,我们基于BatchNorm2d对通道重要度 γ \gamma γ参数进行稀释训练。对BatchNorm2d及它的前后层也需要进行剪枝。主要针对有参数的层:Conv2d、BatchNorm2d、Linear。但是我们不会对Pool2d 层进行剪枝,因为Pool2d只用来做下采样,没…

【进阶篇】MySQL分库分表详解

文章目录 0. 前言1. 垂直分库分表2. 水平分库分表 1. 理解过程及实现方案问题讨论衍生出分库分表策略借助成熟组件使用分库分表阶段完成后面临的问题1. 异地多活问题2. 数据迁移问题3. 分布式事务问题4. join查询的问题 分库分表的策略实现示例 2. 参考文档 0. 前言 假设有一个…

46、SpringBoot输入校验--JSR 303

★ Spring Boot的输入校验 springboot支持两种校验方式&#xff1a;1. Spring原生提供的 Validation&#xff0c;这种验证方式需要开发者手写验证代码&#xff0c;比较繁琐。就是普通的if判断2. 使用JSR 303的校验&#xff0c;这种验证方式只需使用注解、即可以声明式的方式进…

4G版本云音响设置教程阿里云平台版本

4G版本云音响设置教程介绍 第一章 介绍了在阿里云物联网平台生一个设备使用的三元素 第二章 转换阿里云三元素 为MQTT参数&#xff0c;并下载到设备中 第三章 阿里云物联网套件协议使用说明&#xff0c;如何发送数据至设备并播放 本文目录引导 目录 4G版本云音响设置教程介…

利用frps搭建本地自签名https服务的透传

nginx的搭建就不介绍了&#xff0c;教程很多&#xff0c;基本上油手就会。 在本例中&#xff0c;frp服务器的域名是 www.yourfrp.com&#xff0c;同时也是反向代理nginx服务器; 本地网站要用的域名&#xff1a; test.abcd.com 请事先将 test.abcd.com 解析到 frp所在服务器…

大数据面试题:MapReduce压缩方式

面试题来源&#xff1a; 《大数据面试题 V4.0》 大数据面试题V3.0&#xff0c;523道题&#xff0c;679页&#xff0c;46w字 可回答&#xff1a;1&#xff09;Hadoop常见的压缩算法有哪些&#xff1f; 问过的一些公司&#xff1a;网易云音乐(2022.11)&#xff0c;阿里(2020.…

重写 UGUI

重写Button using UnityEngine; using UnityEngine.UI; public class MyButton : Button {[SerializeField] private int _newNumber; }using UnityEditor;//编辑器类在UnityEditor命名空间下。所以当使用C#脚本时&#xff0c;你需要在脚本前面加上 "using UnityEditor&q…

华为云云服务器评测 [Vue3 博物馆管理系统] 使用Vue3、Element-plus菜单组件构建轮播图

系列文章目录 第一章 定制上中下&#xff08;顶部菜单、底部区域、中间主区域显示&#xff09;三层结构首页 第二章 使用Vue3、Element-plus菜单组件构建菜单 第三章 使用Vue3、Element-plus菜单组件构建轮播图 [第四章 使用Vue3、Element-plus菜单组件构建组图文章] 华为云云…

如何中mac上安装多版本python并配置PATH

摘要 mac 默认安装的python是 python3&#xff0c;但是如果我们需要其他python版本时&#xff0c;该怎么办呢&#xff1f; 例如&#xff1a;需要python2 版本&#xff0c;如果使用homebrew安装会提示没有python2。同时使用python --version 会发现commond not found。 所以本…

uniapp小程序单页面改变手机电量,头部通知的颜色效果demo(整理)

onShow(){ // 改变电池的颜色 wx.setNavigationBarColor({ frontColor: ‘#ffffff’, //只支持两种颜色 backgroundColor: ‘#ffffff’, animation: { duration: 1 } }) }

Android 13 - Media框架(9)- NuPlayer::Decoder

这一节我们将了解 NuPlayer::Decoder&#xff0c;学习如何将 MediaCodec wrap 成一个强大的 Decoder。这一节会提前讲到 MediaCodec 相关的内容&#xff0c;如果看不大懂可以先跳过此篇。原先觉得 Decoder 部分简单&#xff0c;越读越发现自己的无知&#xff0c;Android 源码真…

IDEA设置文件编码

IDEA设置文件编码 File->Settings->Editor->File Encodings 均设置为utf-8 新项目 设置 文件编码 点击New Projects Setup 再点击Settings for New Projects File->Settings->Editor->File Encodings 均设置为utf-8

【Linux】文件

Linux 文件 什么叫文件C语言视角下文件的操作文件的打开与关闭文件的写操作文件的读操作 & cat命令模拟实现 文件操作的系统接口open & closewriteread 文件描述符进程与文件的关系重定向问题Linux下一切皆文件的认识文件缓冲区缓冲区的刷新策略 stuout & stderr 什…

windows笔记本远程连接如何打开任务管理器?

参考素材&#xff1a; https://jingyan.baidu.com/article/8275fc86a97f5207a03cf6cd.html https://www.anyviewer.cn/how-to/ctrl-alt-delete-remote-desktop-6540.html 网上查了很多方法&#xff0c;都说ctrlaltend可以解决这个问题。 但是笔记本键盘上没有end键。 继续查了一…

【MATLAB第71期】基于MATLAB的Abcboost自适应决策树多输入单输出回归预测及多分类预测模型(更新中)

【MATLAB第71期】基于MATLAB的Abcboost自适应决策树多输入单输出回归预测及多分类预测模型&#xff08;更新中&#xff09; 一、效果展示&#xff08;多分类预测&#xff09; 二、效果展示&#xff08;回归预测&#xff09; 三、代码获取 CSDN后台私信回复“71期”即可获取下…

c++:QT day2 信号和槽

1.多态&#xff1a; 静态多态&#xff1a;函数的重载 动态多态&#xff1a;程序运行 多态的实现:父类的指针或引用&#xff0c;指向或初始化子类的对象&#xff0c;调用子类对父类重写的函数&#xff0c;进而展开子类的功能 2.虚函数&#xff1a;用virtua关键字修饰的函数是虚函…