嵌入式学习——ARM学习(2)——汇编学习

news2024/11/15 17:28:24

工具:Keil-uVision5

1、汇编

1.1 汇编的组成

指令:汇编语言的核心部分,表示 CPU 可以执行的操作,如数据传输、算术运算、逻辑运算等。

操作数:指令中用于指定操作对象的数据,可以是寄存器、内存地址或立即数。

标签:用于标识代码中的特定位置,通常用于跳转和循环控制。

伪指令:不直接对应于机器指令的指令,用于控制汇编过程,如 .data、.text、.global 等。

注释:用于解释代码,通常以特定符号(如 ;、@)开头和/* */,汇编器在处理时会忽略这些内容。

段:汇编程序通常分为不同的段,如代码段(.text)、数据段(.data)和堆栈段(.bss),每个段有不同的用途。

宏:一种用于简化代码的工具,可以定义一组指令并在需要时调用。

这些组成部分共同构成了汇编语言,使得程序员能够以更接近人类理解的方式编写与机器语言相对应的代码

1.2 汇编指令格式

1. **标签(可选)**:
   - 用于标识指令的位置,后面跟一个冒号(`:`)。
   - 例如:`loop_start:`

2. **操作码(Opcode)**:
   - 指令的名称,表示要执行的操作。
   - 例如:`MOV`、`ADD`、`SUB`。

3. **操作数**:
   - 指令的参数,指定操作的对象,可以是寄存器、立即数或内存地址。
   - 例如:`R0`、`R1`、`#5`。

4. **条件码(可选)**:
   - 用于指定指令的执行条件,通常放在操作码前。
   - 例如:`EQ`(相等)、`NE`(不相等)。

5. **注释(可选)**:
   - 用于解释指令的功能,通常以 `@` 开头,汇编器会忽略这些内容。
   - 例如:`@ 这是一个注释`。

### 示例格式:
```assembly
label:   opcode   operand1, operand2  @ 注释
```

### 示例指令:
```assembly
loop_start:   MOV     R0, #5          ; 将 5 移动到 R0 寄存器
               ADD     R1, R0, #10    ; 将 R0 和 10 相加,结果存储到 R1
```

在这个示例中:
- `loop_start` 是标签。
- `MOV` 和 `ADD` 是操作码。
- `R0`、`R1` 和 `#10` 是操作数。
- 注释解释了每条指令的功能。

2、汇编指令

2.1 数据搬移指令

2.1.1 MOV指令

格式:

        mov{条件码} 目标寄存器,操作数

解释:

        条件码可有可无,当存在条件码时,如果条件满足,则将操作数数值搬移到目标寄存器

主要功能:

        1、将数据从一个寄存器传送到另一个寄存器。

        2、将一个常数值传送到寄存器中。

        3、当 PC( R15)用做目的寄存器时,可以实现程序跳转。如“ MOV PC, LR”,所以这种跳转可以实现子程序调用及从子程序返回,代替指令“ B, BL”。       

        4、当 PC 作为目标寄存器且指令中 S 位被设置时,指令在执行跳转操作的同时,将当前处理器模式的 SPSR 寄存器的内容复制到 CPSR 中。这种指令“ MOVS PC LR”可以实现从某些异常中断中返回。

eg:

        MOV R0, #5      ; 将立即数 5 移动到 R0

2.1.2 MVN 指令

格式:

         mvn{条件码} 目标寄存器,操作数

解释:

        将操作数按位取反后的结果搬移到目标寄存器中

主要功能:

        1、MVN 是反相传送( Move Negative)指令。它将操作数的反码传送到目的寄存器。

        2、MVN 指令多用于向寄存器传送一个负数或生成位掩码。

        3、MVN 指令将 shifter_operand 表示的数据的反码传送到目的寄存器 Rd,并根据操作结果更新 CPSR 中相应的条件标志位。

主要作用:

        1、向寄存器中传送一个负数。

        2、生成位掩码( Bit Mask)。

        3、求一个数的反码。

eg:

        MVN R0, #0xfffffffe     ; 把0x00000001  移动到 R0

2.1.3立即数

在 ARM 汇编语言中,立即数(Immediate Value)是指在指令中直接给出的常量值,而不是存储在寄存器或内存中的值。立即数通常用于算术运算、逻辑运算和数据传输等操作。

数据的处理方式

假设存在立即数x,将x循环右移偶数位y,将会得到一个0-255内的数据z.此时,就说明x是一个立即数,当x当作指令的一部分执行时,实际保存到指令空间的时数据y和数据z,在指令空间的[7:0]保存的是数据z,[11:8]保存的是数据(32-y)/2.

立即数的特点

1. 直接使用:立即数可以直接在指令中使用,无需先加载到寄存器。

2. 范围限制:在 ARM 中,立即数的表示通常有范围限制,具体取决于指令的类型。例如,某些指令的立即数可以是 8 位、12 位或 16 位。

3. 前缀:在 ARM 汇编中,立即数通常以 # 符号开头。

立即数的判断:

1.直接写完编译,出错就不是立即数

2.判断一个数据是不是立即数时,只需要判断这个数据或者它的取反值能不能循环右移偶数位得到一个0-255内的数据,如果能吗,这个数据就是一个立即数

如果想要将一个非立即数放到寄存器中,可以使用一个伪指令LDR

格式:ldr 目标寄存器,=数据

eg: LDR R0,=0X12345678

2.2 移位运算指令

2.2.1 LSL 逻辑左移

解释:

        一个32位数据进行逻辑左移,最高位移出,最低位补0

格式:

        lsl{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        MOV R0, #0XFF

        LSL R1, R0, #0X4        @0xFF0

C语言语法也可以

        LSL R1, R0, #(0x1<<2)        @0xFF0

2.2.2 LSR 逻辑右移

解释:

        一个32位的数据进行逻辑右移,最低位移出,最高位补0

格式:

        lsr{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        MOV R0, #0XFF

        LSR R1, R0, #0X4        @0xF

2.2.3 ASR 算数右移

解释:

        一个32位的数据进行逻辑右移,最低位移出,最高位(30)补0(31符号位不变)

格式:

        asr{条件码} 目标寄存器,第一操作寄存器,第二操作数

2.2.4 ROR 循环右移

解释:

        一个32位的数据进行循环右移,最低位移出,补到最高位

格式:

        ror{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        MOV R0, #0XFF

        ROR R1, R0, #0X4        @0xF000000F

2.3 位运算指令

2.3.1 and 与运算

解释:

        将第一操作寄存器的数据和第二操作数进行与运算,得到的结果保存到目标寄存器中

格式:

        and{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        and r2,r1,r3 ;r2 = r1&r3

2.3.2 orr 或运算

解释:

        将第一操作寄存器的数据和第二操作数进行或运算,得到的结果保存到目标寄存器中

格式:

        orr{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        orr r2,r1,r3 ;r2 = r1 | r3

2.3.3 eor 异或运算

解释:

        将第一操作寄存器的数据和第二操作数进行异或运算,得到的结果保存到目标寄存器中

格式:

        eor{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        eor r2,r1,r3 ;r2 = r1 ^ r3

2.3.4 mvn取反运算

查看2.1.2

2.3.5 bic 按位清零(遇1清0)

解释:

        将第一操作寄存器的数据和第二操作数进行按位清零运算,得到的结果保存到目标寄存器

格式:

        BIC{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        bic r0, r0, #0x1011 ;清除r0中的位12、 4和0位,保持其余的不变

        bic r1, r2, r3 ;将 r3 和 r2 做“逻辑与”操作,结果保存到 r1 中

2.4 算数运算指令

2.4.1 add 加法

解释:

        将第一操作寄存器的数值+第二操作数,结果保存到目标寄存器中,如果+s,运算结果会影响到CPSR条件位

格式:

        add{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数

2.4.1.1 adc 加法

解释:

        目标寄存器 = 第一操作寄存器 + 第二操作数 + cpsr的c位数值

格式:

        adc{条件码} 目标寄存器,第一操作寄存器,第二操作数

2.4.2 sub 减法

解释:

        将第一操作寄存器的数值-第二操作数,结果保存到目标寄存器中,如果+s,运算结果会影响到CPSR条件位

格式:

        sub{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数

2.4.2.1 sbc 减法

解释:

        目标寄存器 = 第一操作寄存器 - 第二操作数 - cpsr的c位的取反值

格式:

       sbc{条件码} 目标寄存器,第一操作寄存器,第二操作数

2.4.3 rsb 翻转减

解释:

        将第二操作数-第一操作寄存器的数值,结果保存到目标寄存器中,如果+s,运算结果会影响到CPSR条件位

格式:

        rsb{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数

2.4.4 mul 乘法

解释:

        将第二操作数*第一操作寄存器的数值,结果保存到目标寄存器中

格式:

        mul{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数

2.5 比较指令

cmp 比较

解释:

        将第一操作寄存器的数值和第二操作数进行比较 比较指令的本质是比较的两个数据进行减法运算,并且运算的结果会影响到CPSR寄存器的条件位

格式:

        cmp 第一操作寄存器,第二操作数

注意:

        比较指令经常和条件码一起使用,即先比较两个数的大小,再下面执行的指令后加上条件码,根据条件码 对应的条件是否满足决定这条指令是否执行

图表:

2.6 跳转指令

2.6.1 b

解释:

        跳转到指定的标签下执行,当程序发生跳转时lr不保存程序的返回地址

格式:

        b 标签

2.6.2 bl

解释:

        跳转到指定的标签下执行,当程序发生跳转时lr保存程序的返回地址

格式:

        bl 标签

2.7 内存读写指令

通过指令将一个寄存器的数据向内存中写或者从内存中读取数据保存到一个寄存器中

2.7.1 单寄存器内存读写指令 STR / LDR

向内存中写 str

        1、str 目标寄存器,[保存目标地址的寄存器] @将目标寄存器4字节数据写入到目标地址对应的内存中

        2、strh 目标寄存器,[保存目标地址的寄存器] @将目标寄存器2字节数据写入到目标地址对应的内存中

        3、strb 目标寄存器,[保存目标地址的寄存器] @将目标寄存器1字节数据写入到目标地址对应的内存中

从内存中读 ldr 

        1、ldr 目标寄存器,[保存目标地址的寄存器] @从目标地址对应的内存中读取4字节数据写入到目标寄存器

        2、ldrh 目标寄存器,[保存目标地址的寄存器] @从目标地址对应的内存中读取2字节数据写入到目标寄存器

        3、ldrb 目标寄存器,[保存目标地址的寄存器] @从目标地址对应的内存中读取1字节数据写入到目标寄存器

拓展:

        str 目标寄存器,[保存目标地址的寄存器,#操作数] @将目标寄存器的数据写入到目标地址+操作数为首地址的内存中

        ldr 目标寄存器,[保存目标地址的寄存器,#操作数] @从目标地址+操作数为首地址的内存中读取数据写入到目标寄存器

        str 目标寄存器,[保存目标地址的寄存器],#操作数 @将目标寄存器的数据写入到目标地址为首地址的内存中,然后保存目标地址的寄存器数值变为原来的数据+操作数大小

        ldr 目标寄存器,[保存目标地址的寄存器],#操作数 @从目标地址为首地址的内存中读取数据写入到目标寄存器,然后保存目标地址的寄存器数值变为原来的数据+操作数大小

2.7.2 批量寄存器内存读写指令  STM / LDM

写:将多个寄存器的数据写入到指定内存中

读:从指定的内存中读取数据写入到多个寄存器

2.7.2.1 stm / ldm

写:

        stm 保存目标地址的寄存器,{寄存器列表} @将寄存器列表中各个寄存器的数据写入到目标地址对应的内存中

读:

        ldm 保存目标地址的寄存器,{寄存器列表}  @从目标地址对应的内存中读取数据保存到寄存器列表中的各个寄存器中

注意:

        1.寄存器列表中寄存器编号如果连续,可以用-表示一个寄存器范围 {r1-r5} 如果寄存器编号散乱,则用 ,分隔每一个寄存器 {r1,r3,r5,r7} {r1-r3,r5,r7}

        2.无论寄存器列表中寄存器的顺序是什么样的,低内存地址始终和小编号寄存器对应

2.7.2.2 地址增长方式

当指定一个内存基地址后,我们对这个基地址内存进行读写操作,然后根据地址增长方式可以让我们操作的基地址发生变化

格式:

        stm{地址增长后缀} 保存目标地址的寄存器!,{寄存器列表}

解释:

        将寄存器列表中的寄存器数据写入到目标地址中时,保存目标地址的寄存器保存的目标地址会发生对应的改变

后缀:

        ia:先向目标地址对应的内存中写入数据,然后保存目标地址的寄存器保存的目标地址数值+4

        ib:先让保存目标地址的寄存器保存的地址+4,再向目标地址对应的内存中写入数据         da:先向目标地址对应的内存中写入数据,然后保存目标地址的寄存器保存的目标地址数值-4

        db:先让保存目标地址的寄存器保存的地址-4,再向目标地址对应的内存中写入数据

ldm同上

2.7.3 栈内存读写指令

操作栈内存需要知道栈顶的地址,而栈顶地址保存在SP寄存器中

栈类型

增栈:每次压栈结束后栈指针寄存器保存的栈顶地址往高地址方向增长

减栈:每次压栈结束后栈指针寄存器保存的栈顶地址往低地址方向增长

空栈:每次压栈结束后栈指针寄存器指向的栈顶空间没有保存的有效数据

满栈:每次压栈结束后栈指针寄存器指向的栈顶空间存在有效数据

栈可以分为空增栈(EA)、空减栈(ED)、满增栈(FA)、满减栈(FD)

ARMv7-a架构处理器默认使用满减栈实现压栈和出栈

压栈命令:

        push {寄存器列表} @将寄存器列表中每一个寄存器的数值进行压栈

出栈命令:

        pop {寄存器列表}

手动实现满减栈

方法1、db后缀压栈 ia后缀出栈

eg:

        

方法2、使用满减栈对应的后缀fd实现

eg:

        

2.8 状态寄存器传送指令 MRS / MSR

内核中存在一个寄存器CPSR,这个寄存器保存的是当前程序的状态,我们可以通过状态寄存器传送指令实现对当前程序状态的读取以及修改程序的状态

MRS 目标寄存器,CPSR 

        @读取CPSR保存的状态值到目标寄存器中

MSR CPSR,操作数

        @将操作数写入到CPSR寄存器中进而修改程序的状态

注意:

        在特权模式下可以通过修改cpsr寄存器的数值切换到USER模式

        但是USER模式下无法通过修改CPSR数值切换到特权模式

        想要在user模式下切换到其他模式,只要发生对应的事件后处理器自动进入对应的模式

2.9 软中断产生指令

2.9.1 软中断

swi 中断号

        @中断号是一个12位的立即数,不同的中断号用来标识不同的中断

2.9.2 异常模式和异常源

异常模式:当发生了异常之后处理器进入的工作模式就是异常模式

异常源:引发处理器进入异常模式的源头叫做异常源

5种异常模式和7种异常源

异常模式异常源
IRQ        

产生了一个IRQ中断

FIQ

产生了一个FIQ中断

UNDEF

处理器遇到未定义指令导致程序中止

SVC

复位

产生软中断

ABORT

存取数据异常导致程序中止

存取指令异常导致程序中止

2.9.3 异常向量表

在 ARM 架构中,异常向量表(Exception Vector Table)是一个特殊的内存区域,用于处理各种异常和中断。异常向量表的每个条目对应于特定类型的异常,指向处理该异常的代码(即异常处理程序)。

异常向量表的结构

在 ARM 体系结构中,异常向量表通常位于内存的起始地址(例如,地址 0x00000000)。每个异常向量的大小通常为 4 字节(32 位),因此每个异常向量表的条目可以容纳一个指向异常处理程序的地址。

地址异常进入模式
0x00000000复位管理模式
0x00000004未定义指令未定义模式
0x00000008软件中断管理模式
0x0000000C中止(预取)中止模式
0x00000010中止(数据)中止模式
0x00000014保留保留
0x00000018中断 IRQ中断模式
0x0000001C快中断 FIQ快中断模式

.section .vectors
    .word _reset_handler      ; 复位向量
    .word _undefined_handler   ; 未定义指令向量
    .word _swi_handler         ; 软件中断向量
    .word _prefetch_abort_handler ; 预取异常向量
    .word _data_abort_handler  ; 数据异常向量
    .word 0                    ; 保留
    .word _irq_handler         ; IRQ 向量
    .word _fiq_handler         ; FIQ 向量

.section .text
_reset_handler:
    ; 复位处理代码
    B main                    ; 跳转到主程序

_undefined_handler:
    ; 未定义指令处理代码
    B .

_swi_handler:
    ; 软件中断处理代码
    B .

_prefetch_abort_handler:
    ; 预取异常处理代码
    B .

_data_abort_handler:
    ; 数据异常处理代码
    B .

_irq_handler:
    ; IRQ 处理代码
    B .

_fiq_handler:
    ; FIQ 处理代码
    B .

2.9.4 异常处理流程(四大步,三小步)

1、保存程序当前的工作状态到对应的异常模式的SPSR寄存器中

2、修改SPSR

        1.修改程序的工作模式为对应的异常模式[4:0]

        2.修改程序的工作状态为ARM状态[5] @ARM为0,thumb为1

        3.根据当前异常的优先级设置程序合适的中断禁止[7:6] @1为忽略

3、将程序的返回地址保存到对应的异常模式的LR寄存器(L14)

4、修改PC寄存器的值为当前异常在异常向量表中的位置

注意:该步骤为处理器自动完成

2.9.5 异常返回工作

注意:该步骤为 程序员手动完成

1、恢复程序状态 SPSR => CPSR

2、返回主程序执行 LR => PC

2.9.6 测试代码

.text  
.global _start 
            

_start:
@初始化异常向量表
    b main
    b .
    b do_swi  @产生软中断后跳到这里执行
    b .
    b .
    b .
    b .
    b .
main:
@初始化SVC模式下的栈
    ldr sp,=0X40000020
    @切换程序的工作模式到user模式
    MSR CPSR,#0x10
    @初始化user模式下的栈
    ldr sp,=0X40000020
    @主程序执行
    mov r1,#1
    mov r2,#2
    @产生一个软中断
    swi 1
    add r3,r1,r2
    b loop
    

@软中断异常处理程序
do_swi:
@压栈保护现场
    stmfd sp!,{r1,r2,lr}
    mov r1,#3
    mov r2,#4
    mul r4,r1,r2
    ldmfd sp!,{r1,r2,pc}^  @异常返回
    
loop:
    b loop
.end 
    

3、混合编程

要想实现C和汇编的混合编程必须遵循ATPCS规范。

ATPCS : ARM-Thumb Procedure Call Standard
 

int add(int i,int j) {
    return i+j;
}

将汇编的标签当作C语言的函数使用

将C语言的函数当作汇编的标签使用

函数参数的传递采用R0-R3进行传递,如果参数的个数大于4个通过压栈的方式进行传递

函数的返回值通过R0返回,如果函数的返回值大于4个字节通过r0-r1返回

ATPCS规范中规定ARM采用满减栈。

3.1 汇编调用C语言的函数 

#include <stdio.h>

// 声明汇编函数
extern int add_func(int a, int b);

int main() {
    int result = add_func(5, 10); // 调用汇编函数
    printf("Result: %d\n", result);
    return 0;
}
*****汇编文件**********
.text    
.global _start  
            
_start: 

    @ 1. 初始化栈指针,C代码运行必须有栈
    ldr sp, =0x40000820
    
    @ 2. 汇编调用c函数 
    @ 2.1 给C的函数传递实参值
    mov r0, #3   @ a = 3
    mov r1, #4   @ b = 4
   
    @ 2.2 汇编调用c的函数
    bl add_func
    
    @ 2.3 函数的返回通过r0返回,查看r0寄存器中的值

loop:   
        b loop  

.end

3.2 c语言调用汇编标签

.text    
    .globl _start  
                
    _start: 
    
        @ 1. 初始化栈指针,C代码运行必须有栈
        ldr sp, =0x40000820
        
        @ 2. 汇编调用c,跳转到main函数
        b main
    .end
********c文件************
// 使用extern对函数进行声明
extern int add_func(int a, int b, int c, int d);

int sum = 0;
int main()
{
 // 在c代码中调用汇编代码
 sum = add_func(1,2,3,4);
 while(1);
 return 0;
}
********汇编文件**********
.text 
.global add_func  @ 将add_func函数声明为全局
    
    add_func:
        add r0, r0, r1
        add r0, r0, r2
        add r0, r0, r3
        mov pc, lr
.end

3.3 c语言内联汇编

c语言中直接使用汇编的语法

通过asm关键字进行修饰

asm volatile(
    "汇编指令\n\t"     //"\n\t"表示一条指令的结束
    .....
    :输出列表  //指令结果的输出值
    :输入列表  //指令的数据输入
    :破坏列表  //破坏列表指定我们当前被修改的资源 memory
);
********汇编启动文件*******
.text    
.globl _start  
            
_start: 

    @ 1. 初始化栈指针,C代码运行必须有栈
    ldr sp, =0x40000820
    
    @ 2. 汇编调用c,跳转到main函数
    b main
.end
**********c语言文件***********
 // 内联汇编 
int add_func2(int a, int b, int c, int d)
{
 int sum = 0;
 // 使用汇编实现求和
 asm volatile(  "add r0, r0, r1\n\t"  "add r0, r0, r2\n\t"  "add r0, r0, r3\n\t"  :"=r"(sum)  :"r"(a),"r"(b),"r"(c),"r"(d)  :"memory" );
 return sum;
}
 //"=r"(sum)表示输出从寄存器中放到变量sum中
// "r"(a) 指定输入从变量a中获取放到通用寄存器
 //"memory"声明使用内存

// 使用extern对函数进行声明
extern int add_func(int a, int b, int c, int d);

int sum = 0;
int main()
{
 // 调用内联汇编的函数 
 sum = add_func2(5,6,7,8);
 
 // 在c代码中调用汇编代码
 sum = add_func(1,2,3,4);
 while(1);
 return 0;
}
 *********汇编文件*************
 .text 
.global add_func  @ 将add_func函数声明为全局
    
add_func:
        add r0, r0, r1
        add r0, r0, r2
        add r0, r0, r3
        mov pc, lr
.end
    

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

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

相关文章

Qt第二课----信号和槽

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

带你速通C语言——静态变量(14)

使用静态变量 (static) 在 C 语言中是管理函数内部状态或跨函数调用保持数据的一种有效方式。理解静态变量如何工作可以帮助你编写更加复杂和可控的程序。 1.静态变量的特性 静态变量有几个关键特性&#xff1a; 持久性&#xff1a;静态变量在函数多次调用之间保持其值。它们…

霍尼韦尔护眼大路灯怎么样?书客、月影、霍尼韦尔实测数据大揭秘

霍尼韦尔大路灯怎么样&#xff1f;目前市面上的大路灯外观几乎都大同小异&#xff0c;但很多品牌在宣传方面做的很牛&#xff0c;消费者在拿到手后的体验却是大打折扣&#xff0c;跟官方宣传的真的是一个天一个地&#xff0c;相差太大。作为一个测评博主&#xff0c;很多人问我…

elasticsearch整合java使用创建索引、指定索引映射、操作添加文档、删除文档、更新文档、批量操作

前言&#xff1a; elasticsearch的整合流程可以参考&#xff1a;Elasticsearch7.15版本后新版本的接入-CSDN博客 索引 1.创建索引 Testpublic void contextLoads() throws IOException {ElasticsearchClient elasticsearchClient elasticSearchConfig.esRestClient();bool…

【Qt】表单布局QFormLayout

表单布局QFormLayout Qt 还提供了 QFormLayout , 属于是 QGridLayout 的特殊情况, 专⻔⽤于实现两列表单的布局. 这种表单布局多⽤于让⽤⼾填写信息的场景. 左侧列为提⽰, 右侧列为输⼊框 例子&#xff1a;使用QFormLayout创建表单 &#xff08;1&#xff09;设置三个label、…

数据分析及应用:如何对试卷得分做min-max归一化处理?

目录 0 问题描述 1 数据准备 2 问题分析 3 小结 0 问题描述 现有试卷信息表examination_info(exam_id试卷ID, tag试卷类别, difficulty试卷难度, duration考试时长, release_time发布时间): 试卷作答记录表exam_record(uid用户ID, exam_id试卷ID, start_time开始作答时…

强者和弱者的区别体现在面对失败上

面对成功&#xff0c;面对日常&#xff0c;每一个人都是谦谦君子温文尔雅&#xff0c;谈论困难挫折&#xff0c;不屑一顾。 这是他们的真实面目吗&#xff1f; 有的是真的&#xff0c;有的是假的。 强者视失败为磨砺意志的砥石&#xff0c;他们勇于承认不足&#xff0c;积极寻…

物联网架构之CDH集群部署

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

中国各地区-交通运输-电信业务总量(1999-2020年)

电信业务总量是指以货币形式表示的电信企业为社会提供的各类电信服务的总数量&#xff0c;包含了各类电信业务&#xff0c;如固定电话、移动电话、数据通信、互联网接入等。 1999-2020年 中国各地区-交通运输-电信业务总量 指标 年份、地区、交通运输-电信业务总量(亿元)。 …

2024年8月29日(harbor似有仓库管理,Docker-compose容器编排)

一、harbor私有仓库管理 yum -y install epel-release yum -y install python2-pip pip install --upgrade pip pip list pip 8x pip install --upgrade pip pip install --upgrade pip20.3 -i https://mirrors.aliyun.com/pypi/simple pip list pip install docker-compo…

基于my Batis优化图书管理系统(总)

1.准备工作 1.1 数据库表设计 -- 创建数据库 DROP DATABASE IF EXISTS book_manage;CREATE DATABASE book_manage DEFAULT CHARACTER SET utf8mb4; use book_manage;-- 用户表 DROP TABLE IF EXISTS user_info; CREATE TABLE user_info (id INT NOT NULL AUTO_INCREMENT,use…

网站建设完成后, 功能性网站如何做seo

功能性网站的SEO优化关注于提高网站在搜索引擎中的排名&#xff0c;从而吸引更多用户并提升用户体验。以下是功能性网站SEO的详细解析&#xff1a; 关键词研究与布局 目标受众分析&#xff1a;了解目标受众的搜索习惯和需求&#xff0c;确定适合的关键词。使用工具如Google Ke…

反弹shell流量分析与检测

常用的隧道技术&#xff1a; 网络层&#xff1a;ipv6、Icmp、gre IPv6隧道&#xff1a;将ipv6报文放入ipv4作为载体进行传输&#xff0c;工具&#xff1a;socat、6tunnel ICMP隧道&#xff1a;将数据放入ping包中进行传输&#xff0c;工具&#xff1a;icmpsh、PingTunnel G…

日本麻将入门(二):牌效率【基础】

基础牌效率 引入 日麻&#xff0c;又称立直麻将。日麻的水平本质上与你是否会立直有很大关系&#xff08;参见常用役种&#xff1a;立直&#xff1a;优点&#xff09;&#xff0c;但立直最大的缺点就是不能副露&#xff0c;导致我们只能通过自己的摸切来完成听牌形的组成。在…

20. 筛选dataframe

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 筛选条件 基本筛选 要筛选DataFrame&#xff0c;首先需要了解筛选条件。Pandas提供了多种筛选条件&#xff0c;包括等于&#xff08;&#xff09;、不等于&#xff08;&#xff01;&#xff09;、大于&#xff08;…

Leetcode Day14排序算法

动态git可以看 :https://leetcode.cn/problems/sort-an-array/solutions/179370/python-shi-xian-de-shi-da-jing-dian-pai-xu-suan-fa/ 选择排序 def selection_sort(nums):n len(nums)for i in range(n):for j in range(i, n):if nums[i] > nums[j]:nums[i], nums[j] …

05.整合Axios+MockJs

1. 前言 作为前后端分离的项目&#xff0c;必不可少的当然是发请求向后端拿数据了&#xff0c; 但是不可能每次等到接口完成我们才开始开发前端&#xff0c;所以使用 mock.js 先模拟后端接口&#xff0c;等后端接口开发完成后&#xff0c;可以无缝衔接&#xff0c;直接替换为真…

EmguCV学习笔记 VB.Net 7.2 特征点检测

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

(24)(24.6) 基于OSD的参数菜单

文章目录 前言 1 Copter默认屏幕 2 Plane默认屏幕 3 实例 前言 这允许使用 ArduPilot 机载 OSD 和 RC 发射机的杆输入设置和调整参数。还有两个额外的 OSD 屏幕可用&#xff08;OSD5 和 OSD6&#xff09;&#xff0c;每个屏幕有 9 个“插槽”来保存参数。屏幕首先显示一组…

taro ui 小程序at-calendar日历组件自定义样式+选择范围日历崩溃处理

taro ui 日历文档 目录 单选标记时间&#xff1a; 效果&#xff1a; template&#xff1a; data&#xff1a; methods: 日历--范围选择&#xff1a; 效果&#xff1a; template&#xff1a; data&#xff1a; methods&#xff1a; 日历--间隔多选&#xff1a;利用标…