嵌入式C语言自我修养:ARM体系结构与汇编语言

news2024/11/20 0:41:24

ARM体系结构

⭐ 关联知识点:指令集

计算机的指令集一般可分为4种:复杂指令集(CISC)、精简指令集(RISC) 、显式并行指令集 (EPIC)和超长 指 令 字 指 令 集(VLIW)。嵌入式用的是RISC指令集,RISC指令集相对于CISC指令集,主要有以下特点

●Load/Store架构,CPU不能直接处理内存中的数据,要先将内存中的数据Load(加载)到寄存器中才能操作,然后将处理结果Store(存储)到内存中。

●固定的指令长度,单周期指令。

●倾向于使用更多的寄存器来存储数据,而不是使用内存中的堆栈,效率更高。ARM指令集虽然属于RISC,但是和RISC相比,有一些差异如下:

●ARM有桶型移位寄存器,单周期内可以完成数据的各种移位操作。

●并不是所有的ARM指令都是单周期的。

●ARM有16位的Thumb指令集,是32位ARM指令集的压缩形式,提

高了代码密度。

●条件执行:通过指令组合,减少了分支指令数目,提高了代码

密度。

●增加了DSP、SIMD/NEON等指令。

关联知识点:用户态、内核态、中断

ARM处理器有多种工作模式,应用程序正常运行时,ARM处理器工作在用户模式,当程序运行出错或有中断发生时,ARM处理器就会切换到对应的特权工作模式。用户模式运行不了特权指令,需要切换到特权模式下才能运行。在ARM处理器中,除了用户模式是普通模式,剩下的几种工作模式都属于特权模式。

在ARM处理器内部,除了基本的算术运算单元、逻辑运算单元、浮点运算单元和控制单元,还有一系列寄存器,包括各种通用寄存器、状态寄存器、控制寄存器,用来控制处理器的运行,保存程序运行时

的各种状态和临时结果。

ARM处理器中的寄存器可分为通用寄存器和专用寄存器两种。

寄存器R0~R12属于通用寄存器,除了FIQ工作模式,在其他工作模式下这些寄存器都是共用、共享的:

  1. R0~R3通常用来传递函数参数
  2. R4~R11用来保存程序运算的中间结果或函数的局部变量等,R12常用来作为函数调用过程中的临时寄存器。

ARM处理器有多种工作模式,除了这些在各个模式下通用的寄存器,还有一些寄存器在各自的工作模式下是独立存在的,如R13、R14、R15、CPSP、SPSR寄存器,在每个工作模式下都有自己单独的寄存器。

  1. R13寄存器又称为堆栈指针寄存器,用来维护和管理函数调用过程中的栈帧变化,R13总是指向当前正在运行的函数的栈帧,一般不能再用作其他用途。
  2. R14寄存器又称为链接寄存器,在函数调用过程中主要用来保存上一级函数调用者的返回地址。
  3. 寄存器R15又称为程序计数器(PC),保存的地址中取的,每取一次指令,PC寄存器的地址值自动增加。CPU一条一条不停地取指令,程序也就源源不断地一直运行下去。在ARM三级流水线中,PC指针的值等于当前正在运行的指令地址+8,后续的32位处理器虽然流水线的级数不断增加,但为了简化编程,PC指针的值继续延续了这种计算方式。
  4. 当前处理器状态寄存器(Current Processor State Register CPSR)主要用来表征当前处理器的运行状态。除了各种状态位、标志位,CPSR寄存器里也有一些控制位,用来切换处理器的工作模式和中断使能控制。

在每种工作模式下,都有一个单独的程序状态保存寄存器(Saved Processor State Register,SPSR)。当ARM处理器切换工作模式或发生异常时,SPSR用来保存当前工作模式下的处理器现场,即将CPSR寄存器的值保存到当前工作模式下的SPSR寄存器。当ARM处理器从异常返回时,就可以从SPSR寄存器中恢复原先的处理器状态,切换到原来的工作模式继续运行。

在ARM所有的工作模式中,有一种工作模式比较特殊,即FIQ模式。为了快速响应中断,减少中断现场保护带来的时间开销,在FIQ工作模式下,ARM处理器有自己独享的R8~R12寄存器。

ARM汇编指令

一个完整的ARM指令通常由操作码+操作数组成,指令的编码格式如下。

<opcode>{<cond>{s} <Rd>,<Rn>{,<operand2>}}

● 使用<>标起来的是必选项,使用{}标起来的是可选项。

● <opcode>是二进制机器指令的操作码助记符,如MOV、ADD这些

汇编指令都是操作码的指令助记符。

● cond:执行条件,ARM为减少分支跳转指令个数,允许类似BEQ、BNE等形式的组合指令。

● S:是否影响CPSR寄存器中的标志位,如SUBS指令会影响CPSR寄存器中的N、Z、C、V标志位,而SUB指令不会。

● Rd:目标寄存器。

● Rn:第一个操作数的寄存器。

● operand2:第二个可选操作数,灵活使用第二个操作数可以提

高代码效率。

存储访问指令

ARM指令集属于RISC指令集,RISC处理器采用典型的加载/存储体系结构,CPU无法对内存里的数据直接操作,只能通过Load/Store指令来实现:当需要对内存中的数据进行操作时,要首先将这个数据从内存加载到寄存器,然后在寄存器中对数据进行处理,最后将结果重新存储到内存中。

ARM处理器属于冯·诺依曼架构,程序和数据都存储在同一存储器上,内存空间和I/O空间统一编址,ARM处理器对程序指令、数据、I/O空间中外设寄存器的访问都要通过Load/Store指令来完成。

LDR R1,[RO]; 将R中的值作为地址,将该地址上的数据保存到R1
STR R1,[RO];  将R中的值作为地址,将R1中的值存储到这个内存地址;每次读写一字节,LDR/STR默认每次读写4字节
LDRB/STRB;  批量加载/存储指令,在一组寄存器和一片内存之间传输数据
SWPR1,R1,[R0];  将R1与R中地址指向的内存单元中的数据进行交换
SWPR1,R2,[R0];  将[R0]存储到R1,将R2写入[R0]这个内存存储单元LDM/STM

LDR/STR、LDM/STM两对指令经常使用。

LDR/STR指令是ARM汇编程序中使用频率最高的一对指令。

LDM/STM指令常用来加载或存储一组寄存器到一片连续的内存,通过和堆栈格式符组合使用,LDM/STM指令还可以用来模拟堆栈操作。

将一组寄存器入栈,或者从栈中弹出一组寄存器。

LDMFD SP!,{R0-R2,R14}; 将内存栈中的数据依次弹出到R14,R2,R1,RO
STMFD SP!{RO-R2,R14}; 将R,R1,R2,R14依次压入内存栈

ARM还专门提供了PUSH和POP指令来执行栈元素的入栈和出栈操作。

PUSH {R0-R2,R14} ;将 R0、R1、R2、R14依次压入栈
POP {R0-R2,R14}   ;将栈中的数据依次弹出到R14、R2、R1、R0

在一个堆栈内存结构中,如果堆栈指针SP总是指向栈顶元素,那么这个栈就是满栈;如果堆栈指针SP指向的是栈顶元素的下一个空闲的存储单元,那么这个栈就是空栈。

每入栈一个元素,栈指针SP都会往栈增长的方向移动一个存储单元。如果栈指针SP从高地址往低地址移动,那么这个栈就是递减栈;如果栈指针SP从低地址往高地址移动,那么这个栈就是递增栈。

数据传送指令

LDR/STR指令用来在寄存器和内存之间输送数据。想要在寄存器之间传送数据,则可以使用MOV指令。

MOV {cond} {s} Rd, operand2
MVN {cond} {S} Rd, operand2

如果想要在寄存器之间传送数据,则可以使用MOV指令。MVN指令用来将操作数operand2按位取反后传送到目标寄存器Rd。

MOV R1,#1   ;将立即数1传送到寄存器R1中
MOV R1, RO   ;将R寄存器中的值传送到R1寄存器中
MOV PC, LR   ;子程序返回
MVN RO,#xFF ;将立即数 0xFF 取反后赋值给 R
MVN RO, R1   ;将R1寄存器的值取反后赋值给R
算术逻辑运算指令

算术运算指令包括基本的加、减、乘、除,逻辑运算指令包括与、或、非、异或、清除等

ADD {cond} {S} Rd, Rn, operand2 ;加法
ADC {cond} {S} Rd, Rn, operand2 ;带进位加法
SUB {cond} {S} Rd, Rn, operand2 ;减法
AND {cond} {S} Rd, Rn, operand2 ;逻辑与运算
ORR {cond} {S} Rd, Rn, operand2 ;逻辑或运算
EOR {cond} {S} Rd, Rn, operand2 ;异或运算
BIC {cond} {S} Rd, Rn, operand2 ;位清除指令

算术逻辑运算指令的基本使用方法及说明如下

ADD R2,R1,#1 ; R2=R1+1
ADC R1,R1,#1 ; R1=R1+1+C(其中C为CPSR寄存器中进位)
SUB R1, R1, R2 ; R1=R1-R2 
SBC R1,R1,R2 ;R1=R1-R2-C
AND RO,RO,#3 ; 保留 R的 bit0和 1,其余位清除
ORR RO,RO,#3 ; 置位 R0的 bit0 和 bit1
EOR RO,RO,#3 ; 反转 R0中的 bit 和 bit1
BIC RO,RO,#3 ;  清除 R0中的 bit0和 bit1

操作数:operand2详解

.......

ARM寻址方式

不同的ARM指令又有不同的寻址方式,比较常见的寻址方式有寄存器寻址、立即寻址、寄存器偏移寻址、寄存器间接寻址、基址寻址、多寄存器寻址、相对寻址等.

寄存器寻址

寄存器寻址比较简单,操作数保存在寄存器中,通过寄存器名就可以直接对寄存器中的数据进行读写.

MOV R1, R2 ;将寄存器R2中的值传送到R1
ADD R1,R2,R3 ;运行减法运算R2-R3,并将结果保存到R1中
立即数寻址
ADD R1,R1,#1 ;将R1寄存器中的值加1,并将结果保存到R1中
ADDR1,R1,#16,20; R1=R1+立即数16循环右移20位
MOV R1,#xFF ;将十六进制常数0xFF写到R1寄存器中
MOV R1,#12 ; 将十进制常数12放到R1寄存器中
寄存器偏移寻址
寄存器间接寻址

寄存器间接寻址主要用来在内存和寄存器之间传输数据。寄存器中保存的是数据在内存中的存储地址,通过这个地址就可以在寄存器和内存之间传输数据。C语言中的指针操作,在汇编层次其实就是使用寄存器间接寻址实现的。

LDRR1,[R2]; 将R2中的值作为地址,取该内存地址上的数据,保存到R1
STRR1,[R2]; 将R2中的值作为地址,将R1寄存器的值写入该内存地址
基址寻址

基址寻址其实也属于寄存器间接寻址。两者的不同之处在于,基址寻址将寄存器中的地址与一个偏移量相加,生成一个新地址,然后基于这个新地址去访问内存.

LDRR1,[FP,#2]     ;将FP中的值加2作为新地址,取该地址上的值保存到R1
LDR R1,[FP,#2]!   ; FP=FP+2,然后将FP指定的内存单元数据保存到R1中
LDRR1,[FP,R0]     ;将FP+R0作为新地址,取该地址上的值保存到 R1
LDR R1,[FP,RO,LSL#2];将FP+R0<<2作为新地址,读取该内存地址上的值保存到 R1
LDR R1,[FP],#2   ;将FP中的值作为地址,读取该地址上的值保存到R1,然后 FP 中的值加 2
STRR1,[FP,#-2]   ;将FP中的值减 2,作为新地址,将 R1中的值写入该地址
STR R1,[FP],#-2] ;将FP中的值作为地址,将R1中的值写入此地址,然后FP中的值减2

基址寻址一般用在查表、数组访问、函数的栈帧管理等场合。根据偏移量的正负,基址寻址又可以分为向前索引寻址和向后索引寻址.

多寄存器寻址

STM/LDM指令就属于多寄存器寻址,一次可以传输多个寄存器的值。

LDMIA SP!,{R0-R2,R14};将内存栈中的数据依次弹出到 R14、R2、R1、RO
STMDB SP!,{RO-R2,R14};将RO、R1、R2、R14 依次压入栈
LDMFD SP!,{R0-R2,R14};将内存栈中的数据依次弹出到 R14、R2、R1、R0
STMFDSP!,{RO-R2,R14};将R、R1、R2、R14 依次压入栈

在多寄存器寻址中,用大括号{}括起来的是寄存器列表,寄存器之间用逗号隔开,如果是连续的寄存器,还可以使用连接符连接,如R0-R3,就表示R0、R1、R2、R3这4个寄存器。LDM/STM指令一般和IA、IB、DA、DB组合使用,分别表示Increase After、Increase Before、Decrease After、Decrease Before。LDM/STM指令也可以和FD、ED、FA、EA组合使用,用于堆栈操作。

相对寻址

相对寻址属于基址寻址,只不过是基址寻址的一种特殊情况。

以PC指针作为基地址进行寻址,以指令中的地址差作为偏移,两者相加后得到的就是一个新地址,然后可以对这个地址进行读写操作。

ARM中的B、BL、ADR指令都是采用相对寻址的。

很多与位置无关的代码,如动态链接共享库,其在汇编代码层次的实现其实也是采用相对寻址的。程序中使用相对寻址访问的好处是不需要重定位,将代码加载到内存中的任何地址都可以直接运行。

ARM伪指令

ARM伪指令不是ARM指令集中定义的标准指令,而是为了编程方便,编译器厂商自定义的一些辅助指令。在程序编译时,这些伪指令会被翻译为一条或多条ARM标准指令。常见的ARM伪指令主要有4个:ADR、ADRL、LDR、NOP。

LDR伪指令

LDR伪指令的主要用途是将一个32位的内存地址保存到寄存器中 。

RISC指令的特点是单周期指令,指令的长度固定。在一个32位的系统中,一条指令通常是32位的,指令中包括操作码和操作数。

指令中的操作码和操作数共享32位的存储空间:一般前面的操作码要占据几个比特位,剩下来的留给操作数的编码空间就小于32位。当编译器遇到MOV R0,#0x30008000这条指令时,后面的操作数是32位,编译器就无法对这条指令进行编码了。为了解决这个难题,编译器提供了一个LDR伪指令来完成上面的功能。

LDR不是普通的ARM加载指令,而是一个伪指令。为了与ARM指令集中的加载指令LDR区别开来,LDR伪指令中的操作数前一般会有一个等于号=,用来表示该指令是个伪指令。通过LDR伪指令,编译器就解决了向一个寄存器传送32位的立即数时指令无法编码的难题。

LDR R,=0x30008000;  有=号的就是伪指令,将立即数x30008000送到R0
LDR RO, =LOOP ;将标号LOOP表示的地址送到R0
LDR RO,[R1] ;R1中的值作为地址,将该地址上的值送到R0
LDR RO,LOOP ;将标号LOOP表示的内存地址上的数据送到R0
ADR伪指令
START
    ADR R0, LOOP     ; 将标号 LOOP 对应的地址加载到寄存器 R0 中
    B END            ; 跳转到 END 标签,结束程序
LOOP
    NOP              ; 一个空操作,作为示例使用
    B LOOP           ; 无限循环

ADR R0, LOOP:这一行使用了 ADR 伪指令,它将标号 LOOP 的地址加载到寄存器 R0 中。ADR 指令会计算当前指令地址与标号 LOOP 之间的偏移量,然后通过相对寻址将 LOOP 的地址加载到 R0 中。ADR伪指令的功能与LDR伪指令类似,将基于PC相对偏移的地址值读取到寄存器中。ADR为小范围的地址读取伪指令,底层使用相对寻址来实现,因此可以做到代码与位置无关。

编译器在编译ADR伪指令时,会首先计算出当前正在执行的ADR伪指令地址与标号LOOP之间的地址偏移OFFSET,然后使用ARM指令集中的一条标准指令代替。

ARM汇编程序设计
ARM汇编程序格式

ARM汇编程序是以段(section)为单位进行组织的。在一个汇编文件中,可以有不同的section,分为代码段、数据段等,各个段之间相互独立,一个ARM汇编程序至少要有一个代码段。可以使用AREA伪操作来标识一个段的起始、段名和段的读写属性。

AREA COPY, CODE, READONLY   ; 当前段属性为代码段,只读,段名为 COPY
    ENTRY
START
    LDR R0, =SRC            ; 将源地址 SRC 加载到寄存器 R0 中
    LDR R1, =DST            ; 将目标地址 DST 加载到寄存器 R1 中
    MOV R2, #10             ; 将值 10 加载到寄存器 R2 中
LOOP
    LDR R3, [R0], #4        ; 从 R0 地址加载一个字到 R3 中,并将 R0 增加 4
    STR R3, [R1], #4        ; 将 R3 中的值存储到 R1 指向的地址,并将 R1 增加 4
    SUBS R2, R2, #1         ; 将 R2 减 1,并设置条件标志
    BNE LOOP                ; 如果 R2 不为 0,则跳转回 LOOP
AREA COPYDATA, DATA, READWRITE ; 数据段,读写权限,段名为 COPYDATA
SRC DCD 1,2,3,4,5,6,7,8,9,0  ; 源数据数组
DST DCD 0,0,0,0,0,0,0,0,0,0  ; 目标数据数组,用于存放复制后的数据
   END

在汇编程序中,使用分号;来注释代码

符号与标号

使用符号来标识一个地址、变量或数字常量。当用符号来标识一个地址时,这个符号通常又被称为标号。符号的命名规则和C语言的标识符命名规则一样:由字母、数字和下画线组成,符号的开头不能使用数字,但标号除外。标号比较任性,标号的开头不仅可以是数字,甚至整个标号可以是一个纯数字。

局部标号

局部标号用于标记程序中的特定位置,以便在该位置附近进行跳转或引用。与全局标号不同,局部标号的作用域通常仅限于当前段或当前范围内,不会影响到其他代码段。局部标号常常使用数字(如 0, 1, 2 等)来命名,并通过特定的格式来引用。

作用域有限:局部标号的作用域只在当前段内有效。这意味着同一个局部标号可以在不同的段中重复使用,而不会产生冲突。

命名方式简单:局部标号通常使用数字命名,如 0, 1, 2 等,这使得它们在短距离跳转和循环结构中非常方便。

引用格式:引用局部标号时,使用 % 符号加上 B(向后搜索)或 F(向前搜索)来确定跳转方向。

如果未指定 FB,编译器会默认先向后搜索,如果找不到,再向前搜索。

MOV R0, #10       ; 将值 10 存入寄存器 R0 中
0   SUBS R0, R0, #1   ; 将 R0 减 1,并设置条件标志
     BNE %B0           ; 如果 R0 不为零,则跳转回局部标号 0
伪操作

伪操作(也称为伪指令)是在汇编语言中使用的一类特殊指令,用来辅助编写和组织汇编代码。与真正的机器指令不同,伪操作并不会直接转换为机器码,而是为汇编器提供了指示,用于控制汇编过程、数据定义、段定义以及程序结构管理等功能。伪操作主要用于提高代码的可读性和结构化,让程序员更方便地编写和管理汇编程序。

符号定义:使用伪操作定义变量、常量或符号。例如,可以使用 EQU 指令将一个符号绑定到一个数值或表达式上。

数据定义:用于定义数据段中的变量和常量。例如,DCDDCBSPACE 等伪操作用来在内存中分配特定的数据空间。

程序结构控制:用于控制汇编程序的流程和组织。例如,AREA 用于定义段(section),ENTRY 用于指定程序的入口点,EXPORTIMPORT 用于声明全局符号和导入其他模块中的符号。

模块化编程:伪操作可以用于定义汇编子程序,并通过伪操作声明全局符号,以便在主程序或其他程序中调用这些子程序。

以下是一些常用的伪操作及其使用方法:

AREA:定义一个段或区域。

AREA MyCode, CODE, READONLY

DCD:定义一个32位常量。

DCD 1, 2, 3, 4

DCB:定义一个字节常量。

DCB 'A', 'B', 'C'

EQU:定义一个符号常量。

MyValue EQU 10

EXPORT:将符号声明为全局符号,允许其他模块访问。

EXPORT MyFunction

IMPORT:导入外部模块中的符号。

IMPORT ExternalFunction

ENTRY:指定程序的入口点。

ENTRY

SPACE:在内存中分配一定数量的字节。

SPACE 16 ;分配16字节

伪操作的作用

伪操作的主要作用是帮助汇编器组织和处理汇编代码,而不是转换成机器码。它们在汇编代码的预处理中起到重要作用,使代码更加灵活和易于维护。例如,使用 EXPORTIMPORT 伪操作,可以轻松实现模块化编程,使得不同的汇编程序模块可以相互调用。

C语言和汇编语言混合编程
ATPCS规则

ATPCS的全称是ARM-Thumb Procedure Call Standard,核心内容就是定义ARM子程序调用的基本规则及堆栈的使用约定等。

如ATPCS规定了ARM程序要使用满递减堆栈,入栈/出栈操作要使用STMFD/LDMFD指令,只要所有的程序都遵循这个约定,ARM程序的格式也就统一了,编写的ARM程序也就可以在各种各样的ARM处理器上运行。

子程序间要通过寄存器R0~R3(可记作a0~a3)传递参数,当参数个数大于4时,剩余的参数使用堆栈来传递。

●子程序通过R0~R1返回结果。

●子程序中使用R4~R11(可记作v1~v8)来保存局部变量。

●R12作为调用过程中的临时寄存器,一般用来保存函数的栈帧

基址,记作FP。

●R13作为堆栈指针寄存器,一般记作SP。

●R14作为链接寄存器,用来保存函数调用者的返回地址,记作LR。

●R15作为程序计数器,总是指向当前正在运行的指令,记作PC。

在ARM平台下,无论是C程序,还是汇编程序,只要大家遵守ARM子程序之间的参数传递和调用规则,就可以很方便地在一个C程序中调用汇编子程序,或者在一个汇编程序中调用C程序。

        AREA    SumFunc, CODE, READONLY
        EXPORT  sum           ; 声明sum为全局函数,使其可以被C代码调用
sum     PROC
       ;输入:R0 和 R1 为两个加数
       ;输出:R0 为和
       ADD     R0, R0, R1    ; 执行加法操作,将结果存储到R0中
       BX      LR            ; 返回调用者
       ENDP
       END

#include <stdio.h>
extern int sum(int a, int b);  // 声明汇编函数

int main() {
    int result = sum(5, 7);    // 调用汇编函数sum
    printf("The result is: %d\n", result);  // 打印结果
    return 0;
}

无条件跳转:BX指令将控制权转移到指定的地址(寄存器中的值)。

在C程序中内嵌汇编代码

为了能在C程序中内嵌汇编代码,ARM编译器在ANSI C标准的基础上扩展了一个关键字__asm。通过这个关键字,可以在C程序中内嵌ARM汇编代码。

int src[10] = {1,2,3,4,5,6,7,8,9};
int dst[10] = {0};
int data_copy_c(void){
    for(int i = 0; i < 10; i++)
        dst[i] = src[i];
    return 0;
}
int data_copy_asm(void){
    __asm
    {
        LDR R0, =src
        LDR R1, =dst
        MOV R2, #10
    LOOP:
        LDR R3, [R0], #4
        STR R3, [R1], #4
        SUBS R2, R2, #1
        BNE LOOP
    }
}
在汇编程序中调用C程序

在汇编程序中同样也可以调用C程序。在调用的时候,我们要注意根据ATPCS规则来完成参数的传递,并配置好C程序传递参数和保存局部变量所依赖的堆栈环境,然后使用BL指令直接跳转即可。

GNU ARM汇编语言

........

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu 22.04 安装 KVM

首先检查是否支持 CPU 虚拟化&#xff0c;现在的 CPU 都应该支持&#xff0c;运行下面的命令&#xff0c;大于0 就是支持。 egrep -c (vmx|svm) /proc/cpuinfo安装 Libvirt apt install -y qemu-kvm virt-manager libvirt-daemon-system virtinst libvirt-clients bridge-uti…

海外企业如何在中国市场“站稳脚跟”?

嘿&#xff0c;企业主们&#xff01;你是否也对中国市场这块诱人的“大蛋糕”心生向往&#xff0c;却又因为种种挑战而犹豫不决&#xff1f;别担心&#xff0c;NetFarmer正是你寻找的那位可靠且充满智慧的探险伙伴。无论是新加坡的企业&#xff0c;还是其他国家的中小企业&…

平凯星辰亮相 2024开放原子开源生态大会,分享开源教育及社区治理经验

9 月 25-27 日&#xff0c;2024 开放原子开源生态大会在北京成功举办&#xff0c;本次大会以“开源赋能产业&#xff0c;生态共筑未来”为主题&#xff0c;由开放原子开源基金会主办&#xff0c;聚焦地方开源实践、企业开源建设思路&#xff0c;围绕开源生态建设&#xff0c;突…

数据校验的总结

业务层进行复杂检查 简单校验交给Controller校验&#xff0c;能流到业务的层的数据就是基本合法 引入依赖&#xff1a;spring-boot-starter-validation 能标注的所有注解在这两个地方看 jakarta.validation.constraints、 org.hibernate.validator.constraints 使用步骤…

Leecode热题100-56.合并区间

以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;intervals [[1,3…

如何获取 uni-app 应用发布所需的证书、私钥与配置文件

引言 在开发和发布iOS应用时&#xff0c;开发者常常会面临一系列复杂的证书、私钥密码以及配置文件的管理问题。这些配置不仅影响到应用的开发调试&#xff0c;还决定了应用是否能够顺利通过审核并发布到App Store。对于使用uni-app进行开发的开发者来说&#xff0c;自动生成的…

IPguard与Ping32:安全性、易用性与稳定性全面对比

在当今这个数据驱动的时代&#xff0c;信息安全已成为企业运营的核心要素。随着网络攻击手段的不断升级&#xff0c;如何确保企业数据的安全、易用与稳定&#xff0c;成为了众多企业关注的焦点。IPguard与Ping32作为两款备受瞩目的防泄密软件&#xff0c;各自以其卓越的性能和独…

VBA高级应用30例应用3Excel中的ListObject对象:选择表的一部分

《VBA高级应用30例》&#xff08;版权10178985&#xff09;&#xff0c;是我推出的第十套教程&#xff0c;教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开&#xff0c;这套教程案例与理论结合&#xff0c;紧贴“实战”&#xff0c;并做“战术总结”&#xff0c;以…

python none代表什么

python中None代表一个特殊的空值&#xff0c;即为一个空对象&#xff0c;没有任何的值。 一般用于assert&#xff0c;判断&#xff0c;函数无返回时的默认&#xff0c;具体如下&#xff1a; 1、assert断言&#xff1a; mylist [a, b, c] >>> assert len(mylist) is…

DELL R720服务器阵列数据恢复,磁盘状态为Foreign

服务器无法正常进入系统&#xff0c;物理磁盘状态变成了Foreign 虚拟磁盘状态变成了Failed 阵列已经丢失了&#xff0c;需要手工强制导入外部配置 单击 Main Menu 屏幕上的 Configuration Management。单击 Manage Foreign Configuration 单击 Preview Foreign Configurati…

用IntStream生成0到n的流,并找出不在numSet中的数字

给定一个包含 [0, n] 中 n 个数的数组 nums &#xff0c;找出 [0, n] 这个范围内没有出现在数组中的那个数。 整体思路就是标题说的那样 首先将nums转化为HashSet&#xff0c;这里也可以不一定是HashSet&#xff0c;是ArrayList也可以 Set<Integer> numSet Arrays.str…

【数据集】建筑节能分析用典型年数据-EPW

【数据集】建筑节能分析用典型年数据-EPW 建筑节能分析用典型年数据-EPWEPW数据获取Climate Consultant软件下载安装及使用软件下载EPW数据读取参考建筑节能分析用典型年数据-EPW EPW数据是由美国能源部开放的专业气象分析软件Energyplus所采用的一个数据格式,几乎涵盖了全球…

设计师找素材,收藏好这8个网站

在设计工作中&#xff0c;找到合适的素材是提升作品质量的关键。我整理了8个设计必备的素材网站&#xff0c;这些平台提供丰富的设计资源&#xff0c;包括模板、图标、照片和插图等&#xff0c;帮助你轻松实现创意构想。无论你是做平面设计、网页设计还是营销材料&#xff0c;这…

【text2sql】新模型架构RESDSQL取得了SOTA效果

这篇论文的标题是《RESDSQL: Decoupling Schema Linking and Skeleton Parsing for Text-to-SQL》&#xff0c;作者是来自中国人民大学信息学院&#xff0c;发表于2023年的AAAI。论文的主要贡献在于提出了RESDSQL框架&#xff0c;该框架通过解耦模式链接和骨架解析来提高Text-t…

开源商城系统crmeb phpstudy安装配置

BOSS让我最快时间部署一套开源商场系统&#xff0c;今天就以crmeb为例。 快速部署在linux中我会首选docker&#xff0c;因为我要在windows中部署&#xff0c;本文就选用phpstudy集成环境做了。 什么是crmeb 我从官网摘点&#xff1a; CRMEB产品与服务 CRMEB通过将CRM&#x…

从零开始学习Vue3

1、Vue3特点&#xff1a;更多的API特性&#xff1b;体积更小&#xff0c;速度更快&#xff1b;解决遗留问题&#xff1b;更加强壮。 2、通过全面学习Vue3&#xff0c;新手与老手的收获&#xff1a; 新手—如何去使用Vue完成项目的开发 老手—深度理解特性背后的原理 3、达到学习…

AVL树学习笔记

目录 1.AVL树的概念 2.AVL树的实现 2.1AVL树的结构 2.2 AVL树的插入 2.2.1 AVL树插入的大致过程 2.2.2 平衡因子的更新 2.2.3 插入节点及更新平衡因子的代码实现&#xff1a; 2.3 旋转 2.3.1 旋转的原则 2.3.2 右单旋 2.3.3 左单旋 2.3.4 左右双旋 2.3.5 右左双旋…

18、电科院FTU检测标准学习笔记-高低温性能试验

作者简介&#xff1a; 本人从事电力系统多年&#xff0c;岗位包含研发&#xff0c;测试&#xff0c;工程等&#xff0c;具有丰富的经验 在配电自动化验收测试以及电科院测试中&#xff0c;本人全程参与&#xff0c;积累了不少现场的经验 ———————————————————…

第二百七十二节 JPA教程 - JPA查询Is Empty示例

JPA教程 - JPA查询Is Empty示例 IS EMPTY运算符是IS NULL的逻辑等价物&#xff0c;但是对于集合。 查询可以使用IS EMPTY运算符或IS NOT EMPTY来检查集合关联路径是否解析为空集合或至少有一个值。 我们可以使用EMPTY来检查属性是否为空。 以下JPQL显示如何使用EMPTY获取员工…

解压缩软件哪个好?不同场景下的最佳选择

解压缩软件在日常工作与生活中发挥着至关重要的作用&#xff0c;从简单的文件解压到处理大型项目&#xff0c;选择一款适合自己的解压缩软件能够大幅提高工作效率。 面对众多解压缩工具&#xff0c;如WinRAR、7-Zip、解压专家、PeaZip等&#xff0c;如何根据不同的使用场景选择…