[知识点] ARM指令集详解

点个小赞关注一波,持续更新……

[专栏]嵌入式软件校招笔记(点击跳转)

[知识点] 嵌入式软件开发知识点学习

[知识点] ARM指令集详解

[知识点] 通讯协议(very重要)

[项目] C++高并发Web服务器+个人改进项目详解

[八股] C/C++基础八股

[八股] C/C++进阶八股

[八股] 计算机网络八股

[八股] 操作系统八股

[八股] 嵌入式系统八股

[八股] Linux系统编程八股

[八股] Linux网络编程八股

秋招嵌入式企业面经

ARM指令集可以分为以下六种

  1. 数据处理指令
  2. 跳转指令
  3. 程序状态寄存器传输指令
  4. Load/Store指令
  5. 协处理器指令
  6. 异常中断指令

1. ARM 数据处理指令

数据处理指令大致可分为3 类:

(1)数据传送指令(如MOV、MVN)

(2)算术逻辑运算指令(如ADD,SUM,AND)

(3)比较指令(如CMP、TST)。

数据运算指令格式

<操作码><目标寄存器><第一操作寄存器><第二操作数>

操作码:表示执行哪种操作

目标寄存器:用于存储运算的结果

第一操作寄存器:存储第一个参与运算的数据(只能是寄存器)

第二操作数:第二个参与运算的数据(可以是寄存器,也可以是立即数)

数据处理指令只能对寄存器的内容操作

所有ARM 数据处理指令均可选择使用S 后缀以影响状态标志。比较指令CMP、CMN、TST和TEQ不需要后缀S,它们会直接影响状态标志。

默认情况下数据运算不会对条件位产生影响,当在指令后加后缀"s"后可以影响

2.1数据传送指令

2.1.1 MOV 数据传送指令

把一个数移动到目标寄存器

格式:

MOV 条件 s

条件: 就表示mov指令是否要执行,如果满足条件就执行mov

s: 是否影响cpsr的值

注:寄存器存储得数据,可以是一个常数,也可以是一个数经过左移右移得到的数据。

将立即数或寄存器(operant2)传送到目标寄存器Rd,可用于移位运算等操作。指令格式如下:

MOV{cond}{S} Rd,operand2
MOV 指令举例如下:
MOV R1#0x10  @ R1=0x10
MOV R0,R1    @ R0=R1
MOVS R3,R1,LSL #2  @ R3=R1<<2,并影响标志位
MOV PC,LR    @ PC=LR ,子程序返回

立即数

由0-255之间的任意数据通过循环右移偶数位得到的数据

立即数的本质就是包含在指令当中的数,属于指令的一部分

立即数的优点:

取址的时候就可以将其读到CPU,不用单独去内存读取,速度快

立即数的缺点

不能是任意的32位的数字,有局限性

2.1.2 MVN 数据非传送指令

将立即数或寄存器(operand2)按位取反后传送到目标寄存器(Rd)。指令格式如下:

MVN{cond}{S} Rd,operand2
MVN 指令举例如下:
MVN R1,#0xFF ;R1=0xFFFFFF00
MVN R1,R2 ;将R2 取反,结果存到R1

2.2算术逻辑运算指令

2.2.1 ADD 加法运算指令

将operand2 数据与Rn 的值相加,结果保存到Rd 寄存器。格式如下:

ADD{cond}{S} Rd,Rn,operand2
ADD 指令举例如下:
ADDS R1,R1,#1 ;R1=R1+1
ADD R1,R1,R2 ;R1=R1+R2
ADDS R3,R1,R2,LSL #2 ;R3=R1+R2<<2

不可以两个数相加 ADD R1, #2, #2是错误的

2.2.2 SUB 减法运算指令

用寄存器Rn 减去operand2,结果保存到Rd 中,格式如下:

SUB{cond}{S} Rd,Rn,operand2
SUB 指令举例如下:
SUBS R0,R0,#1 ;R0=R0-1
SUBS R2,R1,R2 ;R2=R1-R2
SUB R6,R7,#0x10 ;R6=R7-0x10

2.2.3 RSB 逆向减法指令

用寄存器operand2 减法Rn,结果保存到Rd 中,格式如下:

RSB{cond}{S} Rd,Rn,operand2

SUB 指令举例如下:

RSB R3,R1,#0xFF00 ;R3=0xFF00-R1
RSBS R1,R2,R2,LSL #2 ;R1=R2<<2-R2=R2×3
RSB R0,R1,#0 ;R0=-R1

2.2.4 ADC 带进位加法指令

将operand2 的数据与Rn 的值相加,再加上CPSR中的C 条件标志位。结果保存到Rd 寄存器。指令格式如下:

ADC{cond}{S} Rd,Rn,operand2
ADC 指令举例如下:
ADDS R0,R0,R2
ADC R1,R1,R3 ;使用ADC 实现64 位加法,(R1、R0)=(R1、R0)+(R3、R2)

2.2.5 SBC 带进位减法指令

用寄存器Rn 减去operand2,再减去CPSR 中的C条件标志位的非(即若C 标志清零,则结果减去1),结果保存到Rd 中。指令格式如下:

SCB{cond}{S}Rd,Rn,operand2
SBC 指令举例如下:
SUBS R0,R0,R2
SBC R1,R1,R3 ;使用SBC 实现64 位减法,(R1,R0)-(R3,R2)

2.2.6 RSC 带进位逆向减法指令

用寄存器operand2 减去Rn,再减去CPSR 中的C条件标志位,结果保存到Rd 中。指令格式如下:

RSC{cond}{S} Rd,Rn,operand2
RSC 指令举例如下:
RSBS R2,R0,#0
RSC R3,R1,#0 ;使用RSC 指令实现求64 位数值的负数

2.2.7 AND 逻辑与操作指令

将operand2 值与寄存器Rn 的值按位作逻辑与操作,结果保存到Rd中。指令格式如下:

AND{cond}{S} Rd,Rn,operand2
AND 指令举例如下:
ANDS R0,R0,#x01 ;R0=R0&0x01,取出最低位数据
AND R2,R1,R3 ;R2=R1&R3

2.2.8 ORR 逻辑或操作指令

将operand2 的值与寄存器Rn的值按位作逻辑或操作,结果保存到Rd 中。指令格式如下:

ORR{cond}{S} Rd,Rn,operand2
ORR 指令举例如下:
ORR R0,R0,#x0F ;将R0 的低4 位置1
MOV R1,R2,LSR #4
ORR R3,R1,R3,LSL #8 ;使用ORR 指令将近R2 的高8位数据移入到R3 低8 位中

2.2.9 EOR 逻辑异或操作指令

将operand2 的值与寄存器Rn 的值按位作逻辑异或操作,结果保存到Rd中。指令格式如下:

EOR{cond}{S}Rd,Rn,operand2
EOR 指令举例如下:
EOR R1,R1,#0x0F ;将R1 的低4 位取反
EOR R2,R1,R0 ;R2=R1^R0
EORS R0,R5,#0x01 ;将R5 和0x01 进行逻辑异或,结果保存到R0,并影响标志位

2.2.10 BIC 位清除指令

将寄存器Rn 的值与operand2 的值的反码按位作逻辑与操作,结果保存到Rd中。指令格式如下:

BIC{cond}{S}Rd,Rn,operand2
BIC 指令举例如下:
BIC R2,R1,#0x0F ;将R1 的低4 位清零,其它位不变(将#0x0F中值为1的位对应R1中的位清零存到R2,R1并没有变)

LSL(或ASL)左移操作

LSL(或ASL)可完成对通用寄存器中的内容进行逻辑(或算术)的左移操作,按操作数所指定的数量向左移位,低位用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。

MOV R0, R1, LSL #2 @ 将R1中的内容左移两位后传送到R0中。
MOV R1, #1
MOV R2, #2
MOV R3, #3
LSL R1,R2,R3  @ R1=(R2 <<R3) 

LSR右移操作

LSR可完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。

MOV R0, R1, LSL #2 @ 将R1中的内容左移两位后传送到R0中。

MOV R3, #1
MOV R4, #2
MOV R5, #3
LSR R3,R4,R5  @ R3=(R4 >> R5)

2.3 比较指令

2.3.1 CMP 比较指令

指令使用寄存器Rn 的值减去operand2 的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:

本质就是一条减法指令(SUBS),只是没有将运算结果存入寄存器

CMP R1, R2:

=》CPSR寄存器如果标志位Z=1 -> 两者相等,如果Z=0 -> 两者不相等

=》标志位C=0 -> R1<R2,如果C=0或Z=1 -> R1<=R2

=》标志位C=1且Z=0 -> R1>R2

=》标志位C=1 -> R1>=R2

CMP{cond} Rn,operand2
CMP 指令举例如下:
CMP R1,#10 ;R1 与10 比较,设置相关标志位
CMP R1,R2 ;R1 与R2 比较,设置相关标志位
CMP 指令与SUBS 指令的区别在于CMP 指令不保存运算结果。在进行两个数据大小判断时,常用CMP指令及相应的条件码来操作。

2.3.2 CMN 负数比较指令

指令使用寄存器Rn 与值加上operand2 的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行,指令格式如下:

CMN{cond} Rn,operand2
CMN R0,#1 ;R0+1,判断R0 是否为1 的补码,若是Z 置位

CMN 指令与ADDS 指令的区别在于CMN 指令不保存运算结果。CMN指令可用于负数比较,比如CMNR0,#1 指令则表示R0 与-1 比较,若R0 为-(即1 的补码),则Z 置位,否则Z复位。

2.3.3 TST 位测试指令

指令将寄存器Rn 的值与operand2 的值按位作逻辑与操作,根据操作的结果更新CPSR中相应的条件标志位(当结果为0时,EQ位被设置),以便后面指令根据相应的条件标志来判断是否执行。指令格式如下:

TST{cond} Rn,operand2
TST 指令举例如下:
TST R0,#0x01 ;判断R0 的最低位是否为0
TST R1,#0x0F ;判断R1 的低4 位是否为0
TST 指令与ANDS 指令的区别在于TST4 指令不保存运算结果。TST指令通常于EQ、NE条件码配合使用,当所有测试位均为0 时,EQ 有效,而只要有一个测试为不为0,则NE 有效。

2.3.4 TEQ 相等测试指令

指令寄存器Rn 的值与operand2 的值按位作逻辑异或操作,根据操作的结果更新CPSR中相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:

TEQ{cond} Rn,operand2
TEQ 指令举例如下:
TEQ R0,R1 ;比较R0 与R1 是否相等(不影响V 位和C 位)
TST 指令与EORS 指令的区别在于TST 指令不保存运算结果。使用TEQ进行相等测试,常与EQNE 条件码配合使用,当两个数据相等时,EQ 有效,否则NE 有效。

补充:条件码

操作码	条件码助记符	                           标志	含义
0000	EQ	                                   Z=1	相等
0001	NE(Not Equal)	                       Z=0	不相等
0010	CS/HS(Carry Set/High or Same)	       C=1	无符号数大于或等于
0011	CC/LO(Carry Clear/LOwer)	           C=0	无符号数小于
0100	MI(MInus)	                           N=1	负数
0101	PL(PLus)	                           N=0	正数或零
0110	VS(oVerflow set)	                   V=1	溢出
0111	VC(oVerflow clear)	                   V=0	没有溢出
1000	HI(HIgh)		                       C=1,Z=0	无符号数大于
1001	LS(Lower or Same)		               C=0,Z=1	无符号数小于或等于
1010	GE(Greater or Equal)		           N=V	有符号数大于或等于
1011	LT(Less Than)	                       N!=V	有符号数小于
1100	GT(Greater Than)		               Z=0,N=V	有符号数大于
1101	LE(Less or Equal)		               Z=1,N!=V	有符号数小于或等于
1110	AL		                               任何	无条件执行(默认)
1111	NV		                               任何	从不执行
    MOV R1, #1
    MOV R2, #2
    BEQ FUNC  @ if(EQ){B FUNC} 本质:if(Z==1){B FUNC}
    MOV R4, #3
    MOV R3, #3

FUNC:
    MOV R5, #5
    MOV R6, #7
    

2.4 乘法指令

ARM7TDMI(-S)具有32×32 乘法指令、32×32 乘加指令、32×32结果为64 位的乘法指令。

2.4.1 MUL 32 位乘法指令

指令将Rm 和Rs 中的值相乘,结果的低32 位保存到Rd中。(只能是两个寄存器相乘)

指令格式如下:

MUL{cond}{S} Rd,Rm,Rs
MUL 指令举例如下:
MUL R1,R2,R3 ;R1=R2×R3
MULS R0,R3,R7 ;R0=R3×R7,同时设置CPSR 中的N位和Z 位

2.4.2 MLA 32 位乘加指令

指令将Rm 和Rs 中的值相乘,再将乘积加上第3 个操作数,结果的低32位保存到Rd 中。指令格式如下:

MLA{cond}{S} Rd,Rm,Rs,Rn
MLA 指令举例如下:
MLA R1,R2,R3,R0 ;R1=R2×R3+10

2.4.3 UMULL 64 位无符号乘法指令

指令将Rm 和Rs 中的值作无符号数相乘,结果的低32位保存到RsLo 中,而高32 位保存到RdHi 中。指令格式如下:

UMULL{cond}{S} RdLo,RdHi,Rm,Rs
UMULL 指令举例如下:
UMULL R0,R1,R5,R8 ;(R1、R0)=R5×R8

2.4.4 UMLAL 64 位无符号乘加指令

指令将Rm 和Rs 中的值作无符号数相乘,64 位乘积与RdHi、RdLo相加,结果的低32 位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下:

UMLAL{cond}{S} RdLo,RdHi,Rm,Rs
UMLAL 指令举例如下:
UMLAL R0,R1,R5,R8;(R1,R0)=R5×R8+(R1,R0)

2.4.5 SMULL 64 位有符号乘法指令

指令将Rm 和Rs 中的值作有符号数相乘,结果的低32位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下:

SMULL{cond}{S} RdLo,RdHi,Rm,Rs
SMULL 指令举例如下:
SMULL R2,R3,R7,R6 ;(R3,R2)=R7×R6

2.4.6 SMLAL 64 位有符号乘加指令

指令将Rm 和Rs 中的值作有符号数相乘,64 位乘积与RdHi、RdLo,相加,结果的低32位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下:

SMLAL{cond}{S} RdLo,RdHi,Rm,Rs
SMLAL 指令举例如下:
SMLAL R2,R3,R7,R6;(R3,R2)=R7×R6+(R3,R2)

2. ARM 跳转指令

跳转指令用于实现程序流程的跳转,在ARM程序中有两种方法可以实现程序流程的跳转:

  • 使用专门的跳转指令;
  • 直接向程序计数器PC写入跳转地址值。

通过向程序计数器PC写入跳转地址值,可以实现在4GB的地址空间中的任意跳转,在跳转之前结合使用MOV LR,PC等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。

ARM指令集中的跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转,包括以下4条指令。

  • B:跳转指令。
  • BL:带返回的跳转指令。
  • BLX:带返回和状态切换的跳转指令。
  • BX:带状态切换的跳转指令。

3.1 B 跳转指令

跳转到指定的地址执行程序。

B{cond} label

举例如下:

B WAITA ;跳转到WAITA 标号处
B 0x1234 ;跳转到绝对地址0x1234 处

B指令是最简单的跳转指令。一旦遇到一个B指令,ARM处理器将立即跳转到给定的目标地址,从那里继续执行。注意存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是24位有符号数,左移两位后有符号扩展为32位,表示的有效偏移为26位(前后32MB的地址空间)。(跳转到指令B 限制在当前指令的±32Mb 的范围内。)

EQ:equal 相等
NE:not equal,不相等
GT:great than,大于
GE greate equal,大于等于
LT:less than,小于
LE:less equal,.小于等于

3.2 BL 带链接的跳转指令

BL是另一个跳转指令,但在跳转之前,会在寄存器R14中保存PC的当前内容,因此,可以通过将R14的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段。

指令将下一条指令的地址拷贝到R14(即LR)链接寄存器中,然后跳转到指定地址运行程序。

BL{cond} label

举例如下:

BL Label ;当程序无条件跳转到标号Label处执行时,同时将当前的PC 值保存到R14中

跳转指令B 限制在当前指令的±32MB 的范围内。BL 指令用于子程序调用。

3.3 BX 带状态切换的跳转指令

跳转到Rm 指定的地址执行程序,若Rm 的位[0]为1,则跳转时自动将CPSR 中的标志T 置位,即把目标地址的代码解释为Thumb代码;若Rm 的位[0]为0,则跳转时自动将CPSR 中的标志T 复位,即把目标地址的代码解释为ARM代码。指令格式如下:

BX{cond} Rm
举例如下:
ADRL R0,ThumbFun+1
BX R0 ;跳转到R0 指定的地址,并根据R0 的最低位来切换处理器状态

3.4 BLX

BLX目标地址:跳转,改变状态及保存PC值

汇编指令实例

area RESET, code,readonly
code32					  
entry					 
 
start					 
 
	mov r0, #9
	mov r1, #15
loop
	cmp r0,r1 ;判断两个寄存器值是否相等
	beq stop  ;如果相等,结束程序
	cmp r0,r1 ;判断两个寄存器值
	subgt r0,r0,r1 ;如果大于,则执行r0=r0-r1
	sublt r1,r1,r0 ;如果小于,则执行r1=r1-r0
bloop          ;跳转到loop标号继续执行
stop
	end
 
伪代码:
@ if(R1== R2)
@	STOP()
@ else if(R1 >R2)
@ {
@ 	R1 = R1- R2;
@	goto START;
@ }
@ else{
@	R2 = R2 - R1;
@	goto START;
@ }
汇编:
	MOV R1, #1
	MOV R2, #4
START:
	CMP R1, R2
	BEQ STOP
	SUBGT R1, R2, R3
	SUBLT R2, R2, R1
	B START
	
STOP:			@死循环防止程序跑飞
	B STOP
.end			@汇编的结束

3. Load/Store指令

ARM 处理器是冯诺依曼存储结构,程序空间、RAM 空间及IO 映射空间统一编址,除对对RAM 操作以外,对外围IO、程序数据的访问均要通过加载/存储指令进行

ARM 的加载/存储指令是可以实现字、半字、无符/有符字节操作;批量加载/存储指令可实现一条指令加载/存储多个寄存器的内容,大大提高效率。

Load/Store 内存访问指令在 ARM 寄存器和存储器之间传送数据。(访问(读写)内存)ARM 指令中有 3 种基本的数据传送指令。

(1)单寄存器 Load/Store 指令(Single Register),这些指令在 ARM 寄存器和存储器之间提供更灵活的单数据项传送方式。数据项可以是字节、16 位半字或 32 位字。

(2)多寄存器 Load/Store 内存访问指令。这些指令的灵活性比单寄存器传送指令差,但可以使大量的数据更有效地传送。它们用于进程的进入和退出、保存和恢复工作寄存器及复制存储器中的一块数据。

(3)单寄存器交换指令(Single Register Swap)。这些指令允许寄存器和存储器中的数值进行交换,在一条指令中有效地完成 Load/Store 操作。它们在用户级编程中很少用到。它的主要用途是在多处理器系统中实现信号量(Semaphores)的操作,以保证不会同时访问公用的数据结构。

(4)单寄存器的 Load/Store 指令,这种指令用于把单一的数据传入或者传出一个寄存器。支持的数据类型有字节(8 位)、半字(16 位)和字(32 位)。

3.1. 单寄存器的Load/Store指令

如表 3-8 所示列出了所有单寄存器的 Load/Store 指令。

使用单一数据传送指令(STR 和LDR)来装载和存储单一字节或字的数据从/到内存。LDR指令用于从内存读取数据放入寄存器中STR 指令用于将寄存器中的数据保存到内存。指令格式如下:

LDR{cond}{T} Rd,<地址>;加载指定地址上的数据(字),放入Rd中
STR{cond}{T} Rd,<地址>;存储数据(字)到指定地址的存储单元,要存储的数据在Rd中
LDR{cond}B{T} Rd,<地址>;加载字节数据,放入Rd中,即Rd最低字节有效,高24位清零
STR{cond}B{T} Rd,<地址>;存储字节数据,要存储的数据在Rd,最低字节有效

其中,T 为可选后缀,若指令有T,那么即使处理器是在特权模式下,存储系统也将访问看成是处理器是在用户模式下。T在用户模式下无效,不能与前索引偏移一起使用T。

LDR/STR 指令寻址是非常灵活的,由两部分组成,一部分为一个基址寄存器,可以为任一个通用寄存器,另一部分为一个地址偏移量。地址偏移量有以下3种格式:

(1) 立即数。立即数可以是一个无符号数值,这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:

LDR R1,[R0,#0x12] ;将R0+0x12 地址处的数据读出,保存到R1中(R0 的值不变)
LDR R1,[R0,#-0x12];将R0-0x12 地址处的数据读出,保存到R1中(R0 的值不变)
LDR R1,[R0] ;将R0 地址处的数据读出,保存到R1 中(零偏移)

(2)寄存器。寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例值。指令举例如下:

LDR R1,[R0,R2] ;将R0+R2 地址的数据计读出,保存到R1中(R0 的值不变)
LDR R1,[R0,-R2] ;将R0-R2 地址处的数据计读出,保存到R1中(R0 的值不变)

(3)寄存器及移位常数。寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:

LDR R1,[R0,R2,LSL #2] ;将R0+R2*4地址处的数据读出,保存到R1中(R0,R2的值不变)
LDR R1,[R0,-R2,LSL #2];将R0-R2*4地址处的数据计读出,保存到R1中(R0,R2的值不变)

从寻址方式的地址计算方法分,加载/存储指令有以下4 种形式:

(1)零偏移。Rn 的值作为传送数据的地址,即地址偏移量为0。指令举例如下:

LDR Rd,[Rn]

(2)前索引偏移。在数据传送之前,将偏移量加到Rn 中,其结果作为传送数据的存储地址。若使用后缀“!”,则结果写回到Rn中,且Rn 值不允许为R15。指令举例如下:

LDR Rd,[Rn,#0x04]!
LDR Rd,[Rn,#-0x04]

(3)程序相对偏移。程序相对偏移是索引形式的另一个版本。汇编器由PC 寄存器计算偏移量,并将PC寄存器作为Rn 生成前索引指令。不能使用后缀“!”。指令举例如下:

LDR Rd,label ;label 为程序标号,label 必须是在当前指令的±4KB范围内

(4) 后索引偏移。Rn 的值用做传送数据的存储地址。在数据传送后,将偏移量与Rn相加,结果写回到Rn中。Rn 不允许是R15。指令举例如下:

LDR Rd,[Rn],#0x04

地址对准--大多数情况下,必须保证用于32 位传送的地址是32 位对准的。

加载/存储字和无符号字节指令举例如下:

LDR R2,[R5] ;加载R5 指定地址上的数据(字),放入R2 中
STR R1,[R0,#0x04] ;将R1 的数据存储到R0+0x04存储单元,R0 值不变
LDRB R3,[R2],#1 ;读取R2 地址上的一字节数据,并保存到R3中,R2=R3+1
STRB R6,[R7] ;读R6 的数据保存到R7 指定的地址中,只存储一字节数据

加载/存储半字和带符号字节。这类LDR/STR 指令可能加载带符字节\加载带符号半字、加载/存储无符号半字。偏移量格式、寻址方式与加载/存储字和无符号字节指令相同。指令格式如下:


 

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

嵌入式软件校招笔记 文章被收录于专栏

记录本人校招过程中遇到的问题及笔记整理!后续会持续更新

全部评论
很棒,感谢分享😊
5 回复 分享
发布于 2023-10-24 14:41 安徽
码住,明年再学
4 回复 分享
发布于 2023-10-24 14:40 安徽
码住
3 回复 分享
发布于 2023-10-24 14:57 安徽
这里的内存对齐没看懂😂
2 回复 分享
发布于 2023-10-24 23:52 陕西
想在深圳上班的应届生可以来投我们公司哦 岗位很多,欢迎各位小伙伴加入 💼公司岗位
2 回复 分享
发布于 2023-10-26 09:08 江西
mark
2 回复 分享
发布于 2023-10-26 16:29 安徽
码住
点赞 回复 分享
发布于 2023-10-27 23:00 山东

相关推荐

蚂蚁 基架java (n+6)*16 签字费若干
点赞 评论 收藏
分享
头像 会员标识
11-27 17:08
已编辑
牛客_产品运营部_私域运营
腾讯 普通offer 24k~26k * 15,年包在36w~39w左右。
点赞 评论 收藏
分享
09-30 12:39
门头沟学院 C++
点赞 评论 收藏
分享
评论
11
53
分享
牛客网
牛客企业服务