基于ARM深入分析C程序

0 基于韦东山b站视频教程

视频链接:

C语言的本质(基于ARM深入分析C程序)_哔哩哔哩_bilibili

【直播公开课】韦东山老师嵌入式C语言加强,全天8小时直播,吐血整理可以分集观看!_哔哩哔哩_bilibili

1 ARM通用寄存器及其用途

  • R0-R12:这些是通用寄存器,用于存储临时数据和执行算术逻辑操作。在函数调用时,R0-R3通常用作参数寄存器,而R4-R12用于局部变量和临时存储。

  • R13/SP:栈指针寄存器(Stack Pointer Register),用于指向当前栈顶的地址。栈是用于存储程序执行过程中的临时数据,如函数参数、局部变量和返回地址。

  • R14/LR:链接寄存器(Link Register),用于存储子程序调用后的返回地址。当一个函数被调用时,处理器可以将下一条指令的地址存入LR,以便在函数执行完毕后返回到调用点。

  • R15/PC:程序计数器(Program Counter),指向下一条将要执行的指令的地址。PC寄存器是控制流管理的关键部分,决定了程序的执行顺序。

2 ARM基础汇编指令

2.1 数据传输指令

MOV:将一个立即数或寄存器的值复制到另一个寄存器。

MOV R1, #10  ; 将立即数10赋值到寄存器R1
MOV R2, R1   ; 将寄存器R1的值复制到寄存器R2

LDR:从内存中加载数据到寄存器。

LDR R1, [R2]  ; 将寄存器R2指向的内存地址中的值加载到寄存器R1
LDR R0, [R1, #4]     ; 将R1寄存器值加上4作为地址,从内存中读取一个字(32位)到R0寄存器

STR:将寄存器中的数据存储到内存中。

STR R1, [R2]  ; 将寄存器R1的值存储到寄存器R2指向的内存地址
STR R2, [R3, #8]     ; 将R2寄存器的值写入到R3寄存器值加8所指向的内存地址

2.2 算术指令

ADD:将两个寄存器或一个寄存器和一个立即数的值相加,结果存储在目标寄存器中。

ADD R3, R1, R2  ; 将寄存器R1和R2的值相加,结果存储在寄存器R3
ADD R7, R8, #100     ; R7 <- R8 + 100

SUB:从第一个寄存器中减去第二个寄存器的值。

SUB R3, R1, R2  ; 将寄存器R1的值减去R2,结果存储在寄存器R3

MUL:两个寄存器值相乘。

MUL R3, R1, R2  ; 将寄存器R1和R2的值相乘,结果存储在寄存器R3

DIV:第一个寄存器除以第二个寄存器的值。

MOV R4, #2
MOV R5, #4
SDIV R6, R4, R5  ; 将寄存器R4的值除以R5,结果存储在寄存器R6

2.3 逻辑指令

AND:对两个寄存器进行按位与操作。

AND R3, R1, R2  ; 将寄存器R1和R2的值进行按位与操作,结果存储在寄存器R3

ORR:对两个寄存器进行按位或操作。

ORR R3, R1, R2  ; 将寄存器R1和R2的值进行按位或操作,结果存储在寄存器R3

EOR:对两个寄存器进行按位异或操作。

EOR R3, R1, R2  ; 将寄存器R1和R2的值进行按位异或操作,结果存储在寄存器R3

BIC:清除寄存器中指定的位。

BIC R3, R1, #0xFF  ; 清除寄存器R1的最低8位,结果存储在寄存器R3

2.4 数据移位指令

LSL (Logical Shift Left): 将一个寄存器中的值逻辑左移指定位数。

1LSL R0, R1, #3      ; R0 <- R1 << 3

LSR (Logical Shift Right): 将一个寄存器中的值逻辑右移指定位数。

1LSR R2, R3, #2      ; R2 <- R3 >> 2

2.5 比较和跳转指令

CMP:比较两个寄存器的值,并设置条件码。

CMP R1, R2  ; 比较寄存器R1和R2的值

B(或BL):无条件跳转到指定的地址(BL还会将返回地址保存在链接寄存器中)。

B Label  ; 无条件跳转到Label标签处
B 0x12345678     ; 无条件跳转到绝对地址0x12345678
BL subroutine     ; 调用子程序subroutine,返回地址保存在LR

BEQBNEBGTBLT等:根据条件码的值跳转到指定的地址。

BEQ Next  ; 如果相等,跳转到Next标签处
BNE Next  ; 如果不相等,跳转到Next标签处

CMP R0, R1        ; 比较R0和R1,更新条件标志
BGT label         ; 如果R0 > R1,则跳转到label

2.6 其他常用指令

NOP: 执行空操作,常用于占位、填充指令流水线或调试。

NOP                ; 执行一个空指令周期

PUSH/POP: 在栈上压入/弹出多个寄存器的值。

PUSH {R1, R2, R3}  ; 将R1、R2、R3的值依次压入栈
POP {R4, R5, R6}   ; 将栈顶的值依次弹出到R4、R5、R6

3 C语言内存分区(特别重要!!!)

C语言程序在运行时,其内存空间主要可以划分为以下几个区域:

代码区(Text Segment):
用途:存放程序的机器指令,即编译后的二进制代码。这部分内存是只读的,程序执行时不能修改。
生命周期:从程序开始执行到结束,这段区域一直存在。

全局数据区/静态数据区(Data Segment):
用途:存储程序中初始化了的全局变量、静态变量(包括全局静态和局部静态变量)以及常量。初始化为0的全局变量和静态变量也存放在此区域。
生命周期:从程序加载到内存开始,直到程序结束。全局变量和静态变量的值在程序整个生命周期中都保持有效。

未初始化数据区(BSS Segment):
用途:存储未初始化的全局变量和静态变量。系统会自动将这些变量初始化为0(或NULL)。
生命周期:同全局数据区,从程序开始到结束。

栈区(Stack):
用途:主要用于函数调用时的局部变量、函数参数、返回地址等。每当一个函数被调用时,都会为该函数分配一段栈空间,函数执行完毕后,这部分空间会自动释放。
特点:后进先出(LIFO)结构,由编译器自动管理,不需要程序员手动分配和释放。
生命周期:函数调用期间有效,函数返回后,对应的栈空间会被回收。

堆区(Heap):
用途:动态分配的内存,使用malloc、calloc、realloc、free等函数进行管理。主要用于存储程序运行过程中需要动态创建和销毁的数据。
特点:程序员需要手动申请和释放,若忘记释放会造成内存泄漏。
生命周期:取决于程序员何时释放,不释放则直到程序结束才由操作系统回收。

4 栈和堆

4.1 栈(Stack)

定义:栈是一种特殊的内存区域,用于存储局部变量、函数参数以及返回地址等。
特点:
后进先出:栈的访问规则是后进先出,即最后放入栈的元素最先被取出。
自动管理:栈的大小通常由编译器决定,并且由操作系统自动管理。具有相对较高的地址,地址值从高往低分配。
速度较快:由于栈的访问规则简单,处理器通常有专门的指令来支持栈操作,因此访问速度较快。
用途:
存储函数调用时的参数和局部变量。
存储函数的返回地址,以便在函数调用结束后返回到正确的位置。
次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址

4.2 堆(Heap)

定义:堆是用于动态内存分配的内存区域。
特点:
动态分配:堆的大小不固定,可以根据程序的需要动态地分配和释放内存。
手动管理:与栈不同,堆的内存管理需要程序员手动进行,通常使用*malloc(int num);和free(void *address);分配和释放内存。
速度较慢:由于需要动态管理,堆的访问速度通常比栈慢。
用途:
分配在程序运行过程中才知道大小的内存,如用户输入的数据。
分配较大的数据结构,如果使用栈可能会导致栈溢出。

5 变量

5.1 局部变量

局部变量的分配
栈上分配:局部变量通常在函数调用栈上分配空间。当函数被调用时,编译器会为该函数的所有局部变量分配一块连续的内存区域。这块内存从栈顶向下增长,并且在函数返回时自动释放,这意味着局部变量的生命周期仅限于函数的执行期间。

初始化
自动初始化:非静态局部变量如果没有显式初始化,其值是未定义的,这意味着它们可能包含任何随机值。因此,良好的编程实践是总是初始化局部变量,以避免不可预测的行为。

显式初始化:可以通过直接赋予初始值的方式来初始化局部变量,如 int count = 0;。对于静态局部变量,如果不显式初始化,它们会被默认初始化为0(或相应的零值,如NULL对于指针)。

释放
栈上变量的释放:当函数执行结束,无论是正常返回还是通过return语句、异常或中断退出,栈上的局部变量所占用的内存会自动被释放,无需程序员手动干预。

5.2 全局变量

全局变量的分配: 全局变量在整个程序的生命周期中都存在,它们在程序的数据段(data segment)中分配空间。这意味着,一旦程序开始执行,全局变量就已经分配好了内存,直到程序结束才被释放。
初始化:如果全局变量在定义时没有明确赋值,那么它们会被自动初始化为默认值。对于基本数据类型,整型和字符型默认初始化为0,浮点型初始化为0.0,布尔型初始化为false。指针类型初始化为空指针(NULL)。
如果给出了初始值,那么变量会被初始化为指定的值。
作用域: 全局变量在整个文件或程序中都是可见的,除非被局部变量遮蔽。可以在多个文件间通过extern关键字声明来共享全局变量。

5.3 静态变量

静态变量可以分为静态全局变量静态局部变量两种,它们的分配和初始化有所不同:

静态全局变量:
分配与初始化: 同全局变量一样,静态全局变量也在数据段分配空间,并且如果没有初始化,默认值规则同样适用。但它们的作用域仅限于定义它们的文件中(内部链接)。
静态局部变量:
分配: 静态局部变量在栈上并不分配空间,而是在数据段分配,这意味着即使离开其定义的函数,这些变量仍然存在并保持其值。
初始化: 和静态全局变量一样,静态局部变量如果没有初始化,默认也会被赋予相应的零值。每次函数调用时,不会重新初始化,其值会在函数调用间保持。
作用域: 仅限于定义它们的函数或代码块内,但生命周期却是整个程序运行期间。

6 函数

7 指针

8 结构体

9 联合体

10 位域

11 头文件

12 链表

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值