精华内容
下载资源
问答
  • ARM栈与栈指令

    千次阅读 2018-06-19 19:13:08
    一、定义:(Stack)是限定仅在一端进行插入或删除操作的线性表。因此,对来说,可以进行插入或删除操作的...从栈顶的定义来看,栈顶的位置是可变的。空栈时,栈顶和底重合;满时,栈顶离底最远。ARM为堆...

    一、定义:栈(Stack)是限定仅在一端进行插入或删除操作的线性表。

    因此,对栈来说,可以进行插入或删除操作的一端端称为栈顶(top),相应地,另一端称为栈底(bottom)。不含元素的空表称为空栈。由于堆栈只允许在一端进行操作,因而按照后进先出(LIFO-Last In First Out)的原理运作。从栈顶的定义来看,栈顶的位置是可变的。空栈时,栈顶和栈底重合;满栈时,栈顶离栈底最远。ARM为堆栈提供了硬件支持,它使用一个专门的寄存器(堆栈指针)指向堆栈的栈顶。而且7种模式都有各自独立的堆栈指针,也就是有各自独立的堆栈空间。

    二、如何描述一个栈

    准确描述一个栈的特点需要两个参数

    栈地址的增长方向:ARM将向高地址增长(也叫做向上增长)的栈称为递增栈(ascendant Stack),将向低地址增长(也叫做向下增长)的栈称为递减栈(descendantStack)

    栈指针的指向位置:ARM将栈指针指向栈顶元素位置的栈称为满栈(Full Stack),讲栈指针指向即将入栈的元素位置的栈称为空栈(Empty Stack)

    根据栈地址增长方向雨栈指针指向位置的不同,自然可以将栈分为四类:


    下图描述了四种不同类型的栈,其中虚线部分表示即将入栈的元素。

    三、栈指令

    栈的操作指令无非两种:入栈和出栈,由于ARM描述了四种不同类型的栈,因此对应的栈指令一共有8条。


    STM:(STore Multiple data)表示存储数据,即入栈。
    LDM:(LoaD Multiple data)表示加载数据,即出栈。

    一般情况下,可以将栈操作指令分解为两步微指令:数据存取和栈指针移动。这两步操作的先后顺序和栈指针的移动方式由栈的类型决定。

    ARM中存在一组缓冲区操作指令和栈指令是一一对应的,他们完成相同的功能。这些指令含义的区别来源于对存取操作的缓冲区指针地址增长方向,以及存取操作和缓冲区指针移动的先后顺序决定的。这个和前面描述的栈类型的分类原则十分相似。

    四、实例

    虽然ARM的栈类型和相关的操作指令比较繁琐,但是实际上最常用的还是和x86指令集相同的栈类型:栈向低地址方向增长,且栈指针指向栈顶元素的位置,即ARM的FD栈。因此最常见的ARM栈指令操作是STMFD和LDMFD。

    例如入栈指令:

    STMFD SP,{R0-R3}

    实际的微指令操作为:

    [SP-4]  <=  R3
    [SP-8]  <=  R2
    [SP-12] <=  R1
    [SP-16] <=  R0

    在ARM的指令系统中,递减栈入栈操作的参数入栈顺序是从右到左依次入栈,而参数的出栈顺序则是从左到右的逆操作。对于递增栈,相应的操作则全部取反。 
    例如出栈指令:

    LDMFD SP,{R4-R7}

    实际的微指令操作为:

    [SP]    =>  R4
    [SP+4]  =>  R5
    [SP+8]  =>  R6
    [SP+12] =>  R7

    上述的入栈和出栈指令其实仅仅对栈做了存取操作,并未真正改变SP指针的值。正常情况下,我们希望对栈操作后能自动修改栈指针SP的值,使用如下指令可以达到该目的。

    STMFD SP!,{R0-R3}

    对应的微指令操作为:

    [SP-4]  <=  R3
    [SP-8]  <=  R2
    [SP-12] <=  R1
    [SP-16] <=  R0
    SP      =  SP - 16

    同样的:

    LDMFD SP!,{R4-R7}

    对应的微指令操作为:

    [SP]    =>  R4
    [SP+4]  =>  R5
    [SP+8]  =>  R6
    [SP+12] =>  R7
    SP      =  SP + 16

    参考链接:

    https://www.cnblogs.com/fanzhidongyzby/p/5250116.html

    https://www.cnblogs.com/barrychen528/articles/3088328.html



    展开全文
  • : 通过os自动分配和释放,用来存放函数参数值、...因为内存分配内置于计算机CPU的指令集,所以它效率很高,但是分配内存量有局限。 堆: 通过new、malloc、realloc分配内存,可以被GC回收内存。 ...

    栈:

    通过os自动分配和释放,用来存放函数的参数值、局部变量的值等。因为栈内存的分配内置于计算机CPU的指令集,所以它的效率很高,但是分配内存量有局限。

    堆:

     通过new、malloc、realloc分配的内存,可以被GC回收内存。

    展开全文
  • 大部分程序代码到物理机目标代码或虚拟机能执行的指令集之前,都需要经过以下过程:如今,基于物理机、Java虚拟机,或者非Java其他高级语言虚拟机语言,大多数都会遵循这种基于现代经典编译原理思路,在...

    解释执行

    Java被人定位于“解释执行”的语言。在jdk1.0时,定义还算准确,但后来当主流虚拟机中都包含了即使编译器后,Class文件中的代码
    大部分的程序代码到物理机的目标代码或虚拟机能执行的指令集之前,都需要经过以下过程:

    这里写图片描述

    如今,基于物理机、Java虚拟机,或者非Java的其他高级语言虚拟机的语言,大多数都会遵循这种基于现代经典编译原理的思路,在执行前先对程序源码进行词法分析和语法分析处理,把源码转为抽象语法树。

    在Java中,Javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树,再遍历语法树生成线性的字节码指令流的过程,因为这一部分是在Java虚拟机之外进行的,而解释器在虚拟机的内部,所以Java程序的编译就是半独立的。

    基于栈的指令集架构

    Java编译器输出的指令流,基本上是一种基于栈的指令集架构,指令流中的指令大部分都是零地址指令,他们依赖操作数栈进行工作。

    基于栈的指令集主要优点就是可移植,使用栈架构指令集,用户程序不会直接使用寄存器,就可以由虚拟机实现来自行决定把一些访问最频繁的数据放到寄存器中以获取尽量好的的性能。

    栈架构指令集的主要缺点是执行速度相对来说会稍慢一些,主要是因为指令数量和内存访问导致的。

    基于栈指令的解释器执行过程

    public int calc()
    {
        int a=100;
        int b=200;
        int c=300;
        return (a+b)*c;
    }  

    字节码指令

    public int calc();
        Code
        Stack=2.Locals=4,Args_size=1
        0:     bitpush  100
        2:     istroe_1
        3:     sipush   200
        6:     istroe_2
        7:     sipush   300
        10:    istore_3
        11:    iload_1
        12:    iload_2
        13:    iadd
        14:    iload_3
        15:    imul
        16:    ireturn

    javap提示这段需要栈深度为2的操作数栈和4个Slot的局部变量空间。

    首先执行偏移地址为0的指令,bipush指令将单字节的整型常量池推入操作数栈,100指的是推送的常量值。
    这里写图片描述

    执行偏移地址为2的指令,istroe_1指令是将操作数栈顶的值出栈并存放到第1个局部变量Slot中,后续4条指令都是在做同样事情。
    这里写图片描述

    iload_1指令是将局部变量表第1个Slot中的整型值复制到操作数栈顶。
    这里写图片描述

    iload_2指令同样是将第2个Slot中整型值复制到操作数栈顶。
    这里写图片描述

    iadd指令的作用是将操作数栈中两个数出栈,做加法,然后将结果重新入栈。
    这里写图片描述

    iload_3是将局部变量表中第3个Slot入栈,此时操作数栈中为两个整数。
    这里写图片描述

    ireturn指令是方法返回指令之一,将结束方法执行返回操作数栈顶值
    这里写图片描述

    上面的过程只是一种概念模型,虚拟机最终会对执行过程进行一些优化来提高性能。主要是虚拟机中解析器和即使编译器都会对输入的字节码进行优化

    参考《深入理解Java虚拟机》

    展开全文
  • (1)子程序的调用:在跳往某个子程序前,会先将该程序(非跳往的子程序)下条指令的地址存放在堆栈中,直到子程序执行完毕,然后从堆栈中将地址取出,恢复原来程序的执行现场; (2)处理递归调用:和子程序的调用...
    • 栈的几个常用的应用场景:

    (1)子程序的调用:在跳往某个子程序,会先将该程序(非跳往的子程序)下条指令的地址存放在堆栈中,直到子程序执行完毕,然后从堆栈中将地址取出,恢复原来程序的执行现场;

    (2)处理递归调用:和子程序的调用相似,只是除了要存放下一条指令的地址,还要将参数、区域变量等数据存入堆栈中。

    (3)表达式的转换(尤其是“ 中缀表达式 转 后缀表达式 ”)及求值。这个是面试的高频知识点。

    (4)其他数据结构的算法,如二叉树的遍历、图的深度优先搜索DFS等。


    • 数组栈的定义与基本操作:

    定义一个ArrayStack类表示数组栈:

    (1)、因为是数组栈, 因此需要定义3个基本属性:

    1、栈的最大容量:maxSize ;

    2、数组实体: int[ ] stack ;  

    3、栈顶指针:int top = -1 ;

    (2)、判断栈空:top == -1 ; 判断栈满:top == maxSize - 1;   

    (3)、入栈要先判断栈是否满,出栈要判断栈是否空,这是定义方法的临界条件

    (4)、遍历数组栈,直接采用for循环,且只能从栈顶(top)自顶向下进行定义。

    具体代码如下:

    //定义一个ArrayStack 表示栈
    class ArrayStack {
    	private int maxSize ; //栈的最大容量
    	private int[] stack ; //数组模拟栈,数据放在该数组中
    	private int top = -1 ;
    	//构造器
    	public ArrayStack(int maxSize){
    		this.maxSize = maxSize;
    		stack = new int[this.maxSize] ; 
    	}
    	
    	//判断栈满
    	public boolean isFull(){
    		return top == maxSize - 1 ;
    	}
    	//判断栈空
    	public boolean isEmpty(){
    		return top == -1 ;
    	}
    	
    	//入栈
    	public void push(int value){
    		//入栈前先判断栈是否满
    		if(isFull()) {
    			System.out.println("栈满!");
    			return ;
    		}
    		top ++ ;
    		stack[top] = value ;
    	}
    	
    	//出栈
    	public int pop(){
    		//出栈前,先判断栈是否空
    		if(isEmpty()){
    			//由于必须要返回值,所以用抛出异常来处理
    			throw new RuntimeException("栈空,没有数据~");
    		}
    		int value = stack[top];
    		top -- ;
    		return value ;
    	}
    	
    	//栈的遍历(只能从栈顶往下遍历)
    	public void list(){
    		if(isEmpty()){
    			System.out.println("没有数据,无法遍历");
    			return ;
    		}
    		for(int i = top ; i >= 0 ; i --){
    			System.out.printf("stack[%d] = %d\n" , i , stack[i] );
    		}
    	}
    	
    	
    }

    main函数测试代码:

    public class ArrayStackDemo {
    
    	public static void main(String[] args) {
    		//测试数组栈是否正确
    		ArrayStack stack = new ArrayStack(4) ; 
    		String Key = "";
    		boolean loop = true ; //控制是否退出菜单
    		Scanner scanner = new Scanner(System.in);
    		//扫描器,检测键盘输入的数据
    		while(loop){
    			System.out.println("show: 表示显示栈");
    			System.out.println("exit: 退出程序");
    			System.out.println("push: 添加数据到栈(入栈)");
    			System.out.println("pop: 从栈取出数据(出栈)");
    			System.out.println("请输入你的选择:");
    			Key = scanner.next() ; 
    			switch (Key) {
    			case "show":
    				stack.list();
    				break;
    			case "push":
    				System.out.println("请输入一个数:");
    				int value = scanner.nextInt() ;
    				stack.push(value);
    				break;
    			case "pop":
    				try {
    					int res = stack.pop();
    					System.out.printf("出栈数据为:%d \n",res);
    				} catch (Exception e) {
    					System.out.println(e.getMessage());
    				}
    				break;
    			case "exit":
    				scanner.close();
    				loop = false ; 
    				break;
    			}
    		}
    		System.out.println("程序退出!");
    	}
    
    }

     

    • 链栈的定义与基本操作

    用链表实现,首先要定义一个节点类SNode

    class SNode{
    	int no ; //节点编号,是节点排序的依据
    	SNode next ; 	
     //构造方法
    	public SNode(int no){
    		this.no = no ; 
    	}
    	@Override
     //为了输出格式更清楚,在此重写toString方法
    	public String toString() {
    		return "SNode [no=" + no + "]";
    	}	
    }

    然后定义链栈类LinkedStack

    (1)结合栈和链表的性质,需要定义三个基本属性:

    1、头节点top,同时充当栈顶指针;

    2、栈的容量: int maxSize ; 

    3、栈中当前元素个数:int count = 0 ;

    (2)栈空与栈满的条件用count来定义;

    (3)push()与pop()均在链表头部进行:push对应插入节点、pop对应删除节点;

    具体代码实现如下:

    class LinkedStack{
    	private SNode top = new SNode(-1);//表示栈顶
    	private int maxSize = 3 ; //假定栈的最大空间为3
    	private int count = 0 ; 	//声明栈的元素个数
    	
        //判断栈满
    	public boolean isFull(){
    		return count == maxSize ;
    	}
        //判断栈空
    	public boolean isEmpty(){
    		return count == 0 ;
    	}
    
        //入栈操作
    	public void push(int value){
    		if(isFull()){
    			System.out.println("栈满,不能插入栈中");
    			return ;
    		}
    		//push用带头结点的链表使用头插法实现
    		SNode temp = new SNode(value);
    		temp.next = top.next;
    		top.next = temp ;
    		count ++ ;
    	}
    
        //出栈操作
    	public int pop(){
    		if(isEmpty()){
    			throw new RuntimeException("栈空,没有数据可出");
    		}
    		//pop操作借助top指针实现
    		SNode temp = top.next ;
    		top.next = temp.next ;
    		count -- ;
    		return temp.no;
    	}
    	
        //显示栈中元素
    	public void list(){
    		if(isEmpty()){
    			System.out.println("没有数据,无法遍历");
    			return ;
    		}
    		SNode temp = top.next;
    		while(temp!=null){
    			System.out.println(" " + temp.no);
    			temp = temp.next ;
    		}
    	}
    }

    main函数及测试代码:

    public class LinkedStackDemo {
    	//链栈的代码实现
    	public static void main(String[] args) {
    		//用链表模拟栈
    		LinkedStack linkedStack = new LinkedStack();
    		String Key = "";
    		boolean loop = true ; 
    		Scanner scanner = new Scanner(System.in);
    		//扫描器,检测键盘输入的数据
    		while(loop){
    			System.out.println("show: 表示显示栈");
    			System.out.println("exit: 退出程序");
    			System.out.println("push: 添加数据到栈(入栈)");
    			System.out.println("pop: 从栈取出数据(出栈)");
    			System.out.println("请输入你的选择:");
    			Key = scanner.next() ; 
    			switch (Key) {
    			case "show":
    				linkedStack.list();
    				break;
    			case "push":
    				System.out.println("请输入一个数:");
    				int value = scanner.nextInt() ;
    				linkedStack.push(value);
    				break;
    			case "pop":
    				try {
    					int res = linkedStack.pop();
    					System.out.printf("出栈数据为:%d \n",res);
    				} catch (Exception e) {
    					System.out.println(e.getMessage());
    				}
    				break;
    			case "exit":
    				scanner.close();
    				loop = false ; 
    				break;
    			}
    		}
    		System.out.println("程序退出!");
    	}
    }

     

    展开全文
  • 直到有一天发生了IRQ低优先级中断区溢出,经过了一番查找知道了,原来这是汇编指令。 伪指令语句没有对应机器指令。汇编程序汇编源程序时对伪指令进行处理,他可以完成数据定义,存储区分配,段定义,段分配...
  • 栈的应用

    2021-04-15 16:07:26
    定义是只允许在一端插入或删除的特定线性表。 的应用场景: 1、子程序的调用:在跳往子程序前,会将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。 2、处理递归调用:和子...
  • 1. 定义数据 db 数据,数据......数据 //伪操作用来定义字节数据,其后每个操作数都占有一个字节(8位)。...db、dw为伪指令用来将其后数据存入指定存储单元中。 db实例: assume cs:daima daima segmen...
  • 本地变量数组包括方法执行所需要所有变量,包括 this 引用,所有方法参数和其他本地定义的变量。对于那些方法(静态方法 static method)参数是以零开始,对于实例方法,零为 this 保留。 所有类型都在...
  • 栈帧:每个栈帧对应一个被调用方法,可以理解为一个方法运行空间。...局部变量表中变量不可直接使用,如需要使用话,必须通过相关指令将其加载至操作数中作为操作数使用。 (局部变量以0,1,2这样顺序存..
  • 2021-03-14 07:43:47
    4、根据栈的定义可知,最先放入中元素在底,最后放入的元素在栈顶。而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除 的应用场景 1、子程序的调用:在跳往子程序前,会先将下个指令的地址...
  • 1.他是一个先入后出的有序的列表 ...1)子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。 2)处理递归调用: 和子程序的调用类似,只
  • 接下来两篇将介绍在KVM中字节是如何执行,这是KVM中比较核心内容,分为两部分来讲,本篇先介绍虚拟机中的栈和帧是如何实现。 首先来看一些全局指针,在头文件kvm/vmcommon/h/interpret.h中定义有以下结构:...
  • 栈的应用

    2021-04-14 16:48:45
    当一个函数被调用时,一个返回地址( 即被调函数一旦执行完,接下去要执行的程序指令的地址)和被调函数的局部变量和形参的值都要存储在递归工作中。当执行一次返回时,被调函数的局部变量和形参的值被恢复为调用...
  • 1.指令的存储 先来看一个简单的代码片段 如果用中文来描述这个过程 a.我定义了一个变量a b.定义一个变量b c.s输出a+b的值 如果你学过汇编,那就知道,其实就是简单的mov,add指令。但是你没学过,就按照上面...
  • assume cs:code code segment ... 我们自定义数据 不是我们自定义的指令  ; d:define w:word start: mov bx,0  mov ax,0  mov cx,8 ; 将数据相加 addNumber: add ax,cs:[bx]  add bx,2...
  • java 栈的基本介绍

    2021-03-06 16:06:27
    根据栈的定义可知,最先放入中元素在底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除 的应用场景 子程序的调用:在跳往子程序前,会先将下个指令的地址存到...
  • 定义 是一种数据结构,LIFO后进先出 操作 问题 CPU如何知道一段空间被当做来使用?...push指令的执行过程 push ax sp = sp-2(从上到下,地址从低到高) 将ax中的内容送入ss:sp指向...
  • 一个时间点上,只会有一个活动栈帧,即只有当前正在执行方法栈帧(栈顶栈帧)是有效,这个栈帧被称为当前栈帧(Current Frame),与当前栈帧对应方法就是当前方法(Current method),定义这个方法类...
  • 栈的概述及入门案例

    2020-07-06 18:30:37
    根据栈的定义可知,最先放入中元素在底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除 图解: 的应用场景: 子程序的调用:在跳往子程序前,会先将下个...
  • (stack)用处

    2020-11-21 21:51:45
    目录 的一个实际需求 的介绍 的应用场景 的一个实际需求 ...子程序的调用:在跳往子程序前,会先将下个指令的地址存在堆栈中,直到子程序执行完成后在将地址取出,以回到原来的程序
  • 代码区:存放程序代码,即CPU执行机器指令,并且是只读。存放在最底层。 常量区:存放常量(程序在运行期间不能够被改变量,例如: 10,字符串常量”aaaa”, 数组名字等) 静态区(全局区):静态变量和...
  • 根据栈的定义可知,最先放入中元素在底,最后放入的元素在栈顶,而删除元素正好相反,最后放入的元素最先删除,最先放入的元素最后删除. 二:的应用场景 子程序的调用:在跳往子程序前,会先将下一个指令的地址存到...
  • 4)根据栈的定义可知,最先放入中的元素在底,最后放入的元素在栈顶,而删除元素恰好想反,最后放入的元素最先删除,最先放入的元素最后删除 的应用场景 1)子程序的调用:在跳往子程序前,会先将下个指令的...
  • (3)根据栈的定义可知,最先放入中元素在底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最后放入的元素最后删除。 2.应用场景 (1)子程序的调用:在跳往子程序前,会先将下个指令的...
  • Java虚拟机和内存模型

    万次阅读 多人点赞 2021-02-18 15:08:57
    1、结合字节码指令理解Java虚拟机和栈帧 栈帧:每个栈帧对应一个被调用...局部变量表:方法中定义的局部变量以及方法参数存放在这张表中,局部变量表中变量不可直接使用,如需要使用话,必须通过相关指令
  • 根据栈的定义可知,最先放入中的元素在底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素先删除,最先放入的元素最后删除 的应用场景举例 子程序的调用:在跳往子程序前,会先将下个指令的地址...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 844
精华内容 337
关键字:

栈指令的定义