【ARM Cortex-M3指南】3:Cortex-M3基础

news2024/12/23 23:45:04

文章目录

  • 三、Cortex-M3基础
    • 3.1 寄存器
      • 3.1.1 通用目的寄存器 R0~R7
      • 3.1.2 通用目的寄存器 R8~R12
      • 3.1.3 栈指针 R13
      • 3.1.4 链接寄存器 R14
      • 3.1.5 程序计数器 R15
    • 3.2 特殊寄存器
      • 3.2.1 程序状态寄存器
      • 3.2.2 PRIMASK、FAULTMASK和BASEPRI寄存器
      • 3.2.3 控制寄存器
    • 3.3 操作模式
    • 3.4 异常和中断
    • 3.5 向量表
    • 3.6 栈存储操作
      • 3.6.1 栈的基本操作
      • 3.6.2 Cortex-M3 栈的应用
      • 3.6.3 Cortex-M3 的双栈模型
    • 3.7 复位流程

三、Cortex-M3基础

3.1 寄存器

前面已经介绍过了,Cortex-M3处理器具有寄存器R0~R15以及多个特殊寄存器。 R0R12为通用目的寄存器,不过有些16位Thumb指令只能访问R0R7(低寄存器),而 32位Thumb-2指令则可以访问所有的寄存器,特殊寄存器具有预定义的功能,并且只能通过特殊寄存器访问指令操作。

3.1.1 通用目的寄存器 R0~R7

通用目的寄存器R0~R7也称作低寄存器,它们可以通过所有的16位Thumb指令和 32位Thumb-2指令访问。这些寄存器都是32位宽的,并且复位值为不可预测的。

3.1.2 通用目的寄存器 R8~R12

寄存器R8~R12也称作高寄存器,它们可以通过所有的Thumb-2指令访问,而有些 16位的Thumb指令则不可以。这些寄存器都是32位宽,并且复位值是不可预测的。

3.1.3 栈指针 R13

R13为栈指针(SP),Cortex-M3中存在两个SP,利用这种结构,我们可以设置两个独立的栈指针。在使用寄存器名为R13时,只能访问当前的SP,要访问另一个的话,只能通过特殊指令

  1. 从通用目的寄存器到特殊寄存器 - MSR
  2. 从特殊寄存器到通用目的寄存器 - MRS

这两个栈指针为:

  1. 主栈指针(MSP):在ARM文献中也叫SP_main,这是默认的SP,用于操作系统内核、异常处理和所有需要特权访问的应用程序代码。
  2. 进程栈指针(PSP):在ARM文献中也叫SP_process,用于基本的应用程序代码(未运行异常处理时)。

image.png

image.png

使用两个SP是没有必要的,简单的应用程序可以完全依赖MSP。SP用于像PUSH和POP之类的栈存储处理。

对于Cortex-M3,访问栈空间的指令为PUSH和POP,汇编语法如下(每个分号[;]之后的文字为注释)。

PUSH {R0}
步骤1:R13 = R13 - 4
步骤2:存储器[R13] = R0

POP {R0}
步骤1:R0 = 存储器[R13]
步骤2:R13 = R13 + 4

Cortex-M3使用满递减的栈处理(本章的“栈存储操作”中有对这方面的详细介绍),因此,当新的数据存入栈后,SP减小。PUSH和POP通常用于在子例程开始时将寄存器内容保存至栈空间,之后会在子例程结束后将寄存器内容从栈中恢复。PUSH和POP可以在一条指令中操作多个寄存器:

subroutine_1
PUSH {R0 - R7, R12, R14}
...
POP {R0 - R7, R12, R14}
BX R14

在程序代码中,可以使用SP(栈指针)代替R13。这就意味着,在程序代码内部,MSP和PSP都可以被称作R13/SP。不过,你可以使用特殊寄存器访问指令(MRS/MSR)来操作特定的SP。

由于寄存器的PUSH和POP操作总是字对齐(0x4,0x8),故SP的第0位和第1位被硬连接为0,并且读出总是0。

image.png

3.1.4 链接寄存器 R14

R14为链接寄存器(LR),在汇编程序中,可以写作R14或LR。LR用于子例程或函数调用时保存返回地址,例如,在使用跳转链接(BL)指令时:
image.png

尽管PC的第0位总是为0(由于指令都是字对齐或者半字对齐的),LR的第0位是可读可写的。这是因为在Thumb指令集中,第0位通常用于指明ARM/Thumb状态,要将 Cortex-M3上实现的Thumb-2程序运行在其他支持Thumb-2技术的ARM处理器上,这个最低位(LSB)需要为可读可写的。

3.1.5 程序计数器 R15

R15就是PC,在汇编代码中可以使用R15或PC进行访问。由于Cortex-M3处理器的流水线特性,在读这个寄存器时,你会发现读出值和正在执行的指令位置不同,通常差4,例如:

image.png

对PC的写操作会引起跳转(但LR不会更新),由于指令地址必须是半字对齐的,PC读出值的LSB(最低位)总是0。不过,对于跳转,不管是通过写PC还是使用跳转指令,目标地址的LSB都应该置1,以指明当前处于Thumb状态;如果为0的话,处理器会试图切换至ARM状态,而Cortex-M3的这一操作会导致错误异常。

3.2 特殊寄存器

Cortex-M3处理器具有多个特殊寄存器:

  • 程序状态寄存器(PSR);
  • 中断屏蔽寄存器(PRIMASK、FAULTMASK、BASEPRI);
  • 控制寄存器(CONTROL)。

特殊寄存器只能通过指令MSRMRS访问,它们没有存储器地址:

  • MSR <special_reg> <reg>:写入特殊寄存器。
  • MRS <reg> <special_reg>:读取特殊寄存器。

3.2.1 程序状态寄存器

PSR分为三个状态寄存器:

  1. 应用程序状态寄存器 - APSR
  2. 中断程序状态寄存器 - IPSR(只读)
  3. 执行程序状态寄存器 - EPSR(只读)

这三个PSR可以通过特殊寄存器访问指令MSRMRS进行整体操作或者分开操作,整体操作时使用寄存器名xPSR

image.png

image.png

image.png

如果将这个寄存器同ARM7的当前程序状态寄存器(CPSR)相比,可能会发现ARM7中用的某些位域已经不存在了。由于Cortex-M3不具有ARM7定义的操作模式,模式(M)位就去掉了;Tumb(T)位变为了第24位;中断状态(I和F)位则被新的中断寄存器(PRIMASK)代替,这些寄存器同PSR是相互独立的。为了便于比较,传统ARM处理器的 CPSR寄存器如图3.5所示。

image.png

3.2.2 PRIMASK、FAULTMASK和BASEPRI寄存器

PRIMASK、FAULTMASK和BASEPRI寄存器用于禁止异常。

寄存器名描述
PRIMASK寄存器中仅有1位,在其置位时,允许不可屏蔽中断和硬件错误异常,其他所有中断和异常都会被屏蔽;默认为0,不屏蔽中断。
FAULTMASK寄存器中仅有1位,在其置位时,允许不可屏蔽中断NMI,其他所有中断和硬件错误处理异常都会被屏蔽;默认为0,不屏蔽中断。
BASEPRI寄存器中最多8位(取决于优先级的实际位宽),定义了屏蔽优先级。在其置位时,相同或者更低等级的所有中断都被禁止,更高优先级的中断仍可执行。如果设置为0,则屏蔽功能禁止(默认)。

在时间敏感的任务中需要暂时禁止中断时,可以使用PRIMASK和BASEPRI寄存器。当一个任务崩溃时,OS可以使用FAULTMASK暂时禁止错误处理。在这种情况下,任务崩溃时可能会产生多个不同错误。内核开始清理操作时,它也许不想被崩溃进程引起的其他错误打断,因此,利用FAULTMASK,OS内核就获得了处理错误状态的时间。

image.png

image.png
image.png

3.2.3 控制寄存器

控制寄存器用于定义特权等级和SP的选择,该寄存器为2位宽。

image.png

  1. CONTROL[1]

对于Cortex-M3,CONTROL[1]位在处理模式时总是0(使用MSP),不过在线程或者基本等级中,它可以是0或者1。

该位只有在内核处于特权线程下才可写,在其他模式下,该位是不允许进行写操作的。除了写这个寄存器外,也可以在异常返回时修改LR的第2位来修改这一位。

  1. CONTROL[0]

CONTROL[0]位只有在特权等级下可写,一旦进入用户等级,要想切换回特权状态,只能触发一次中断并且在异常处理中进行修改。

image.png

3.3 操作模式

当处理器运行在线程模式中时,它可以处在特权或用户等级,不过处理模式只能位于特权等级。当处理器退出复位时,它处于线程模式并且具有特权访问权限。

在用户访问等级(线程模式),对系统控制空间(SCS,存储器空间的一部分,用于配置寄存器和调试部件)的访问是不允许的。另外,访问特殊寄存器的指令(如MSR,访问APSR时除外)也不可以使用。如果用户访问等级的程序要访问SCS或特殊寄存器,错误异常就会产生。

处在特权访问等级的软件可以通过控制寄存器将程序切换至用户访问等级。异常发生时,处理器总会切换至特权状态,并且在退出异常处理时返回到之前的状态。用户程序无法直接通过写控制寄存器切回特权状态,在返回线程模式时,它必须通过设置控制寄存器的异常处理才可以切换至特权访问等级(见图3.7)。

image.png

对特权和用户访问等级的支持提供了一个更加安全和健壮的架构,例如,当用户程序错误时,它不会破坏嵌套向量中断控制器(NVIC)中的控制寄存器。另外,如果存储器保护单元(MPU)存在的话,用户程序对特权进程使用的存储器区域的访问可能会被禁止。

在简单的应用中,无须区分特权和用户访问等级。在这种情况下,也无须使用用户访问等级以及设置控制寄存器。

你可以将用户应用程序的栈同内核栈相分离,这样可以避免用户程序栈的错误操作引起的系统崩溃。根据这种设计,用户程序(运行在线程模式)使用PSP,而异常处理则使用 MSP。SP在进入或离开异常处理时自动切换(见3.6.3节),第8章有这个方面的详细介绍。

控制寄存器定义了处理器的模式和访问等级,当控制寄存器的第0位为0时,异常发生会引起处理器模式的改变(见图3.8和图3.9)。

image.png

当控制寄存器的第0位为1时(运行用户应用程序的线程),异常发生会引起处理器模式和访问等级的改变(见图3.10)。

只有在特权等级中,控制寄存器的第0位才是可编程的(见图2.5)。用户等级的程序要想切换至特权状态,它必须发起一个中断(例如,请求管理调用[SVC])并在中断处理中修改CONTROL[0]。

3.4 异常和中断

Cortex-M3支持多个异常,其中包括固定数量的系统异常和通常被称作IRQ的多个中断。根据个体设计的不同,Cortex-M3微控制器中断输人的数量也有所不同。外设产生的中断,除了系统节拍定时器,也被连接至中断输入信号上。中断输入的数量一般为16或 32,不过,有些微控制器的中断输入可能会更多(或更少)。

除了中断输入之外,处理器还有一个不可屏械中断(NMI)输入信号,NMI的实际使用情况取决于微控制器或片上系统(SoC)的设计。多数情况下,NMI可以连接至看门狗时钟,或者电压监控模块,这样在电压降到一定程度时处理器会收到警告。NMI异常随时可以激活,甚至是在内核刚刚退出复位后。

Cortex-M3支持的异常如表3.4所示。其中多个系统异常为错误处理异常,它们可由许多错误条件触发。NVIC也提供了多个错误状态寄存器,这样异常处理可以根据这些寄存器确定异常的原因。

如果要了解Cortex-M.3异常处理的细节,可以参考第7~9章的内容。

image.png

3.5 向量表

当Cortex-M3的异常发生并被处理器接受时,对应的异常处理就会执行。为了确定异常处理的起始地址,处理器使用了一种向量表机制。向量表是系统存储器中的字数据数组,每个元素代表了一种异常类型的起始地址。向量表的位置是可以重置的,该位置由NVIC中的重定位寄存器决定(见表3.5)。复位后,该重定位寄存器被置为0,因此,向量表在复位后位于地址0x0处。

image.png

例如,若复位的异常类型为1,那么复位向量的地址为1×4(每个字为4字节),也就是 0x00000004,NM1向量(类型2)位于2×4=0x00000008位置。地址0x00000000用于存放 MSP的初始值。

每个异常类型的LSB表示异常是否允许在Thumb状态中执行,由于Cortex-M3只支持Thumb指令,所有的异常向量的LSB都应该置1。

3.6 栈存储操作

对于Cortex-M3,栈的PUSH和POP除了可以被普通软件控制外,还可在进入或退出异常/中断处理时自动执行。本节中,我们来看一下软件栈操作(异常处理期间的栈操作在第9章中介绍)。

3.6.1 栈的基本操作

般来说,栈操作也就是存储器的写或读操作,只是地址被指定为SP。寄存器中的数据由PUSH操作存人栈存储中,而且稍后可以由POP操作恢复到寄存器中。在PUSH和 POP期间,SP的内容自动调整,因此,多次PUSH不会清除之前的压栈数据。

栈的功能为将寄存器内容存到存储器中,以便处理任务结束后它们还可以被恢复。例如,对于每次存储(PUSH),都必须有对应的读(POP),而且POP操作的地址应该同PUSH操作的地址相匹配。使用PUSH/POP指令时,SP自动增加/减小。

当程序控制返回到主程序时,R0~R2的内容同之前的一致,应该注意PUSH和POP的顺序:POP应是PUSH的反顺序。

由于PUSH和POP指令允许多次存储和加载,因此这些操作可以简化。在这种情况下,处理器会将寄存器POP的顺序自动反过来(见图3.12)。

image.png

RETURN可以同POP操作合并在一起,要实现这个目的,可以将LR存入栈中,并且在子例程结束时将其送回PC(见图3.13)。

image.png

3.6.2 Cortex-M3 栈的应用

Cortex-M3使用了一种满递减的栈操作模型,SP指向压入栈存储的最后一个数据,SP在新的PUSH操作前减小。图3.14为指令PUSH{R0}执行的例子。

image.png

对于POP操作,数据被从SP指向的位置中读出,然后SP增加。存储器位置的数据不会变化,但下次PUSH操作发生时则会被覆盖(见图3.15)。

由于每次PUSH/POP操作传输4字节的数据(每个寄存器包含1个字,也就是4字节),SP每次会增加/减小4字节,如果多于1个寄存器需要压栈或出栈则是4的倍数。

在Cortex-M3中,R13被定义为SP。当中断发生时,多个寄存器会自动压栈,在这个过程中,R13会被用作SP。类似地,在退出中断处理时,压栈的寄存器会自动地恢复/出栈,SP的值也会得到调整。

image.png

3.6.3 Cortex-M3 的双栈模型

之前已经提到Cortex-M3具有两个SP:MSP和PSP。实际使用哪个SP由控制寄存器的第1位控制(下面所说的CONTROL[1])。

当CONTROL[1]为0时,线程模式和处理模式都会使用MSP(见图3.16)。按照这种设计,主程序和异常处理共用相同的栈存储空间,这也是上电后的默认设置。

image.png

当CONTROL[1]为1时,线程模式使用PSP(见图3.17)。按照这种设计,主程序和异常处理可以使用独立的栈存储区域。这样用户程序的栈出现错误时,就不会破坏OS使用的栈了(假定用户程序只运行在线程模式,OS内核运行在处理模式)。

image.png

在这种情况下,应该注意的是,自动压栈和出栈机制将会使用PSP,不过异常处理中的栈操作会使用MSP。

也可以直接对PSP和MSP进行读/写操作,而无须考虑R13代表的含义。假定当前处于特权等级,你可以这样访问MSP和PSP的值:

image.png

一般来说,由于栈存储可能会用于存储局部变量,我们不建议在C函数中改变当前选定SP的值。要用汇编访问SP,可以使用MSR和MRS指令:

image.png

通过MRS指令读出PSP的值后,OS可以读出用户应用程序栈中的数据(如SVC操作前的寄存器内容)。另外,OS可以改变PSP指针数值,比如多任务系统上下文切换期间。

3.7 复位流程

在退出复位后,处理器会从存储器中读取两个字:

  1. 地址0x00000000:R13的初始值(SP)。
  2. 地址0x00000004:复位向量(程序执行的起始地址,LSB应该置1表示Thumb状态)。

image.png

这点同传统的ARM处理器不同,之前的ARM处理器从0x0地址开始执行程序,另外,之前的ARM设备中向量表为指令(你必须在此处放入一个跳转指令,异常处理则可以放在另外一个位置)。

对于Cortex-M3,MSP的初始值位于存储器映射的开始处,后面紧接着的是向量表,也就是向量的地址值(在程序执行过程中,向量表稍后可以被重新分配到另外一个地址)。另外,向量表的内容为地址值,而非跳转指令。向量表中的第一个向量(异常类型1)为复位向量,这是在处理器复位后读取的第二块数据。

由于Cortex-M3的栈操作为满递减的(存储前SP减小),SP的初始值应该置为栈顶后的第一个存储器地址。例如,若栈存储区域的范周为0x20007C00到0x20007FFF(1KB),栈的初始值应该置为0x20008000。

向量表在SP的初始值之后,第一个向量为复位向量。应该注意的是,在Cortex-M3中,向量表中的向量地址的最低位应置1,以表明它们为Thumb代码。由于这个原因,前面例子的复位向量为0x101,而启动代码是从0x100地址开始的(见图3.19)。取出复位向量后,Cortex-M3可以从复位向量地址处执行程序,并且开始正常操作。SP需要被初始化,这是因为复位后就可能会发生一些异常(如NMI),这些异常可能是需要栈存储的。

image.png

不同的开发工具在指定SP的初始值和复位向量时的方式可能不同,若要了解这方面更多的信息,最好参考开发工具提供的工程实例。第10章和第20章提供了ARM工具的一些例子,GNU工具链的例子则在第19章。

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

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

相关文章

缓冲流,BufferReader,BufferWriter,案例

IO流的体系 字节缓冲流的作用 提高字节流读取数据的性能 *原理&#xff1a;字节缓冲输入流自带了8Kb的缓冲池&#xff0c;字节缓冲输出流也自带了8kb的缓冲池 构造器说明public BufferedInputStream(InputStream is)把低级的字节输入流包装成一个高级的缓冲字节输入流&#…

RabbitMQ之顺序消费

什么是顺序消费 例如&#xff1a;业务上产生者发送三条消息&#xff0c; 分别是对同一条数据的增加、修改、删除操作&#xff0c; 如果没有保证顺序消费&#xff0c;执行顺序可能变成删除、修改、增加&#xff0c;这就乱了。 如何保证顺序性 一般我们讨论如何保证消息的顺序性&…

【Linux】进程exec函数族以及守护进程

一.exec函数族 1.exec函数族的应用 在shell下敲shell的命令都是在创建shell的子进程。而我们之前学的创建父进程和子进程代码内容以及通过pid与0的关系来让父子进程执行不同的代码内容都是在一个代码文件里面&#xff0c;而shell是如何做到不在一个文件里面写代码使之成为子进…

4- 29

五六月安排 5.12江苏CPC 6.2、6.16、6.30三场百度之星省赛 6月蓝桥杯国赛 7.15 睿抗编程赛道省赛 5 6月两个科创需要申请完软著。 网络技术挑战赛过了资格赛&#xff0c;下面不知道怎么搞&#xff0c;如果参加需要花费很多的时间。 1.100个英语单词一篇阅读&#xff0c;讲了文…

Docker Compose 部署若依前后端分离版

准备一台服务器 本次使用虚拟机&#xff0c;虚拟机系统 Ubuntu20.04&#xff0c;内存 4G&#xff0c;4核。 确保虚拟机能连接互联网。 Ubuntu20.04 安装 Docker 添加 Docker 的官方 GPG key&#xff1a; sudo apt-get update sudo apt-get install ca-certificates curl su…

Hibernate的QBC与HQL查询

目录 1、Hibernate的QBC查询 2、Hibernate的HQL查询 3、NatvieSQL原生查询 1、Hibernate的QBC查询 Hibernate具有一个直观的、可扩展的条件查询API public class Test { /** * param args */ public static void main(String[] args) { Session sessio…

【八股】AQS,ReentrantLock实现原理

AQS 概念 AQS 的全称是 AbstractQueuedSynchronized &#xff08;抽象队列同步器&#xff09;&#xff0c;在java.util.concurrent.locks包下面。 AQS是一个抽象类&#xff0c;主要用来构建锁和同步器&#xff0c;比如ReentrantLock, Semaphore, CountDownLatch&#xff0c;里…

安卓LayoutParams浅析

目录 前言一、使用 LayoutParams 设置宽高二、不设置 LayoutParams2.1 TextView 的 LayoutParams2.2 LinearLayout 的 LayoutParams 三、getLayoutParams 的使用四、setLayoutParams 的作用五、使用 setWidth/setHeight 设置宽高 前言 先来看一个简单的布局&#xff0c;先用 x…

Jackson-jr 对比 Jackson

关于Jackson-jr 对比 Jackson 的内容&#xff0c;有人在做了一张下面的图。 简单点来说就 Jackson-jr 是Jackson 的轻量级应用&#xff0c;因为我们在很多时候都用不到 Jackson 的很多复杂功能。 对很多应用来说&#xff0c;我们可能只需要使用简单的 JSON 读写即可。 如我们…

手撕spring框架(5)

手撕spring框架(5) 相关系列 手撕spring框架&#xff08;1&#xff09; 手撕spring框架&#xff08;2&#xff09; 手撕spring框架&#xff08;3&#xff09; 手撕spring框架&#xff08;4&#xff09; 这是本专题最后一节了&#xff0c;主要是讲述自定义一个注解&#xff0c;实…

QT中的容器

Qt中的容器 关于Qt中的容器类&#xff0c;下面我们来进行一个总结&#xff1a; Qt的容器类比标准模板库&#xff08;STL&#xff09;中的容器类更轻巧、安全和易于使用。这些容器类是隐式共享和可重入的&#xff0c;而且他们进行了速度和存储的优化&#xff0c;因此可以减少可…

HackTheBox_knote

前言 最近打算刷一些内核利用的 CTF 的题目~~~ 题目分析 内核版本&#xff1a;v5.8.3&#xff0c;但是没有开启 cg 隔离smap/smep/kpti/kaslr 全关&#xff0c;可以 ret2usr&#xff0c;所以应该是比较老的题目了&#xff08;&#xff1a;这里很奇怪的是就算设置 kaslr 但是…

虚拟化技术 使用Vsphere Client管理ESXi服务器系统

使用Vsphere Client管理ESXi服务器系统 一、实验目的与要求 1.掌握使用vSphere Client管理ESXi主机 2.掌握将CentOS的安装介质ISO上传到ESXi存储 3.掌握在VMware ESXi中创建虚拟机 4.掌握在所创建的虚拟机中安装CentOS6.5操作系统 5.掌握给CentOS6.5安装VMware Tools 6.掌…

RabbitMQ(Docker 单机部署)

序言 本文给大家介绍如何使用 Docker 单机部署 RabbitMQ 并与 SpringBoot 整合使用。 一、部署流程 拉取镜像 docker pull rabbitmq:3-management镜像拉取成功之后使用下面命令启动 rabbitmq 容器 docker run \# 指定用户名-e RABBITMQ_DEFAULT_USERusername \# 指定密码-e R…

python数据可视化:显示两个变量间的关系散点图scatterplot()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 python数据可视化&#xff1a; 显示两个变量间的关系 散点图 scatterplot() [太阳]选择题 请问关于以下代码表述错误的选项是&#xff1f; import seaborn as sns import matplotlib.pyplot …

EPAI手绘建模APP编辑模型2

⑩ 桥接&#xff0c;选择两个面。桥接完成后&#xff0c;在选择的两个面之间生成了一个边界放样模型&#xff0c;边界放样模型和原模型合并成一个新的模型。 图 213 桥接 ⑪ 移除特征&#xff0c;选择倒圆角面、倒直角面、挖孔面、凸起面&#xff0c;移除。移除特征后&#xff…

图像处理ASIC设计方法 笔记21 标记ASIC的顶层状态机

目录 (一)标记ASIC的工作流程1 ASIC首先从控制寄存器内读出待标记图像的基本参数2若写入了有效的启动命令,则进入下面一帧图像的标记过程。3 ASIC通过接口模块从FIFO1中读取待标记的图像4一帧图像初步标记完成后进行等价表的整理压缩5从临时标记存储器中读取临时标记送入标记…

【iOS】KVC

文章目录 前言一、KVC常用方法二、key与keypath区别key用法keypath用法 三、批量存值操作四、字典与模型相互转化五、KVC底层原理KVC设值底层原理KVC取值底层原理 前言 KVC的全称是Key-Value Coding&#xff0c;翻译成中文叫做键值编码 KVC提供了一种间接访问属性方法或成员变…

数据结构练习题---环形链表详解

链表成环&#xff0c;在力扣中有这样的两道题目 https://leetcode.cn/problems/linked-list-cycle/ https://leetcode.cn/problems/linked-list-cycle-ii/description/ 这道题的经典解法是利用快慢指针&#xff0c;如果链表是一个环形链表&#xff0c;那么快指针(fast)和慢指…

AI图书推荐:AI在语言学习教育领域的应用和挑战

这本书《AI在语言学习教育领域的应用和挑战》&#xff08;AI in Language Teaching, Learning, and Assessment&#xff09;由Fang Pan编辑&#xff0c;出版于IGI Global&#xff0c;主要探讨了人工智能&#xff08;AI&#xff09;在语言教育领域的应用、挑战以及潜在的益处。 …