• 关于

    出栈

    的搜索结果

回答

栈内存一般存储的是函数的调用信息和函数中申明的变量,因为函数的调用是递归的,外层函数一定比内层被调用的函数先加载和执行,而一定等到内层被调用函数结束后才能结束,这个先进后出的机制就是为什么叫栈内存的原因。PS:在编译时编译器会先收集此函数中所有定义的变量,将他们放在函数最前面申请内存,所以他们进出栈的顺序不是你在编写程序时定义的顺序,而是在函数执行前进栈,函数执行完成后出栈。 举个实际的例子吧:假设某个调用过程是这样的 void a() { int p = 1; int q = 2; } void b() { int x = 3; int y = 4; a(); int z = 5; } 那么我们在调用b();的过程中栈内存其实经历了一下变化:([a]、[b]代表a()和b()的基本信息,如程序指针等) 进入b函数时栈底 < 栈顶 (函数信息空间进栈)[b]其他操作无关进出栈,略去 进入a函数时 (函数信息空间进栈)`栈底 < 栈顶[b] < x < y < z < [a]` 为a函数内参数申请空间`栈底 < 栈顶 (参数空间进栈)[b] < x < y < z < [a] < p < q` 完成a函数时 (参数空间出栈)`栈底 < 栈顶[b] < x < y < z < [a]` 退出a函数时 (函数信息空间出栈)`栈底 < 栈顶[b] < x < y < z` 完成b函数时 (参数空间出栈)`栈底 < 栈顶[b]` 退出b函数时 (函数信息空间出栈)栈底 < 栈顶
杨冬芳 2019-12-02 02:25:44 0 浏览量 回答数 0

回答

栈是一种只能在一端进行插入和删除操作的特殊线性表。其中,允许插入和删除的一端称为栈顶,另一端称为栈底。通常,将栈的插入操作称为入栈,删除操作称为出栈。入栈时元素总是放在栈底,而出栈的总是栈顶元素。因此,栈中元素采用的是“后进先出”的方式
帝宇 2019-12-02 01:02:25 0 浏览量 回答数 0

回答

队列和栈都是被用来预存储数据的。 操作的名称不同。队列的插入称为入队,队列的删除称为出队。栈的插入称为进栈,栈的删除称为出栈。可操作的方式不同。队列是在队尾入队,队头出队,即两边都可操作。而栈的进栈和出栈都是在栈顶进行的,无法对栈底直接进行操作。操作的方法不同。队列是先进先出(FIFO),即队列的修改是依先进先出的原则进行的。新来的成员总是加入队尾(不能从中间插入),每次离开的成员总是队列头上(不允许中途离队)。而栈为后进先出(LIFO),即每次删除(出栈)的总是当前栈中最新的元素,即最后插入(进栈)的元素,而最先插入的被放在栈的底部,要到最后才能删除。 原文链接:https://thinkwon.blog.csdn.net/article/details/104390752
剑曼红尘 2020-03-11 11:30:12 0 浏览量 回答数 0

回答

你要算法,从程序中归纳就行了 我想到一种方法,可是有点复杂,要用到树的结构,先给你看个程序,是计算n个元素进栈,可能的出栈系列种数 /*递归求n个元素进栈,可能的出栈系列种数*/ #define N 4 int m=0,a=0,b=N;/*m表示种数,a表示栈中元素个数,b表示外面还有需要进栈的个数*/ main() { inS(a,b);/*首先入栈*/ printf("%d",m); getch(); } int inS(int a,int b)/*入栈*/ { a++;b--;/*入栈栈中元素+1,栈外元素-1 */ if(b>0)/*若栈外有元素,可以入栈*/ inS(a,b); if(a>0)/*若栈中有元素,可以出栈*/ outS(a,b); } int outS(int a,int b)/*出栈*/ { a--;/*出栈栈中元素-1*/ if(a==0&&b==0)/*若栈中元素和栈外元素都为0个*/ { m++;/*则此种情况的序列满足条件,种数+1*/ return; } if(b>0) inS(a,b); if(a>0) outS(a,b); } 若想输出这些序列,可以建立一个二叉树,二叉树的节点为操作(进栈或出栈),从根节点(第一个入栈)到叶子节点(最后一个出栈)的路径就是每个序列的操作序列,每条路径共有2N个节点,分别为每个元素的入栈和出栈操作,然后通过遍历,将这些节点输出即可 建立这棵树可以用上面的递归建立,也可以用下面的方法建立 ①建立一颗深度为2N的满二叉树(根节点深度为1),其中根节点为IN(入栈),其他左子树为IN,右子树为OUT(出栈),这棵树共有2^(2N-1)个叶子节点,用根节点到叶子节点共有2^(2N-1)条路径 ②保留满足下面条件的路径,其他的剔除 1)路径从根到叶出现IN和OUT总次数均为N个 2)路径从根到叶出现 IN次数≥OUT次数(即出栈次数不可能多余入栈次数) 然后输出保留的每条路径上节点类型为OUT的数据,即是出栈序列 剔除方法可由下面方法实现 1根节点赋值为1, 2节点为IN,则此节点赋值为父节点的值+1,节点为OUT,则此节点赋值为父节点的值-1, 3剔除节点的值为负数,或值>N的节点,或叶子节点上的值不=0的路径,剩下的就是满足条件的路径 ------------------------------------------------- 我想到一种方法,可以不用树的结构,只是模拟树,先贴程序 /*从1到N的顺序入栈,输出所有出栈序列*/ #include "math.h" #define N 4 main() { void init(int h[][],int a,int b,int k); int i,j,sum,logo,h[1000][2*N+1],s[N],a,b,n; int M=pow(2,2*N-1);/*共M条路径,每条有2*N个节点*/ /*h[i][]为第i条路径的操作序列,其中h[i][0]为一个标志,标志此序列是否满足条件,是则为1,否为0;从h[1]到h[2*N]为操作序列,1表示入栈,-1表示出栈*/ n=M;/*n为成功的序列计数*/ /*初始化标志和第一个操作,因为第一个操作必定为入栈操作*/ for(i=0;i<M;i++) { h[i][0]=1;/*1表示可以,0表示不可以*/ h[i][1]=1; }/*1表示可以,0表示不可以*/ init(h,0,M-1,2);/*开始初始化第二个操作,不管是否满足条件的序列,都存入数组h中*/ /*剔除不符合要求的序列*/ j=0;/*从第0组至第M组依次检验*/ while(j<M) { logo=0; sum=0;/*计算从h[][1]开始序列的和值*/ for(i=1;i<=2*N;i++) { sum+=h[j][i]; if(sum>N||sum<0)/*若出现入栈次数大于N,或出栈次数多于入栈次数*/ { logo=1; break;/*此序列埠满足条件*/ } } if(logo==1||sum!=0)/*sum和值表示入栈总数减出栈总数,若不为0,即入栈总数不等于出栈总数,则此序列不满足条件*/ { h[j][0]=0;/*当前序列的标志为0,表示不满足条件*/ n--;/*总序列总数-1*/ } j++; } /*输出各序列的出栈序列,s[]是临时模拟栈的,其中变量a表示栈顶(将要插入元素的位置,即栈顶元素的下一位置),b表示将要入栈的元素*/ for(j=0;j<M;j++) { if(h[j][0]==1)/*若序列标志为1*/ { a=0;b=1; for(i=1;i<=2*N;i++) { if(h[j][i]==1&&b<=N)/*若序列操作为1(入栈)*/ { s[a]=b;a++;b++;/*则入栈*/ } if(h[j][i]==-1)/*若序列操作为1(出栈)*/ { printf("%d",s[a-1]);/*输出出栈的元素*/ a--;/*则入栈*/ } } printf("\n"); } } printf("ALL %d !",n);/*所有序列数*/ getch(); } /*递归初始化*/ /*从a到b前一半赋值为1,后一半赋值为-1,由此可得所有的序列*/ /*每步操作(从2到2*N步)都可以任意的为入栈,出栈,然后剔除不满足条件的序列(不满足条件包括栈中没有元素还出栈,或入栈总数超过要出栈的个数,或入栈数与出栈数步相等),剔除的程序段在主函数中*/ void init(int h[1000][2*N+1],int a,int b,int k) { int i,p=a+(b-a)/2; if(k<=2*N) { for(i=a;i<=p;i++) h[i][k]=1; for(i=p+1;i<=b;i++) h[i][k]=-1; init(h,a,p,k+1); init(h,p+1,b,k+1); } }
琴瑟 2019-12-02 01:25:13 0 浏览量 回答数 0

回答

栈的元素从栈底到栈顶输出:最直接的方法是先将栈元素出栈,建立一个新的栈。这样就是将本来的栈中元素的顺序颠倒了,然后对新的栈出栈就获取到想要的结果。
51干警网 2019-12-02 01:34:50 0 浏览量 回答数 0

回答

这种题只能一个一个分析 a 先进栈1,2 出栈2,然后进栈3出栈3,进栈4,5出栈5,接下来无论怎么操作都不会先出1,所以选a 剩下的都这样分析就行。
知与谁同 2019-12-02 01:23:47 0 浏览量 回答数 0

回答

波兰式又称中缀式 逆波兰式又称后缀式 还有一个前缀式 中缀式: 根据算符间的优先关系来确定运算的次序,此外,还应顾及括号规则 如 (A+B)*(C+D) = 运算法则符合我们正常的运算规律 后缀式是有中缀式所得 如 AB+CD+* 运算法则,从从左到右依次进栈,遇见字母入栈,遇见运算符,将前两个字母弹出,进行运算符计算后,将值在入栈,重复此过程 A入栈,B入栈,遇到+,A、B弹出,(A+B)入栈,C入栈,D入栈,遇见+,C、D弹出,(C+D)入栈,遇见*,(A+B)、(C+D)弹出,(A+B)*(C+D)入栈,最终栈里面的只有一个元素,该元素的值就为计算结果 前缀式:就是后缀式的逆序 即*+DC+BA 从右到左依次入栈,只是跟后缀式入栈方向相反,过程相同
小哇 2019-12-02 01:22:22 0 浏览量 回答数 0

问题

java 用两个栈实现队列

看到一个题,是说用栈实现队列的效果,我想的是用两个栈,栈1输出到栈2,再输出,大家帮我看一下,这个程序的最后输出怎么是[b,1],输入的3哪去了? import java.util.Enumeration; import java.util...
蛮大人123 2019-12-01 19:47:57 972 浏览量 回答数 1

回答

二叉树的定义是递归的。 遍历的过程也是递归的。 递归在系统里面的实现是通过堆栈完成的。 在函数体本身入栈的时候,带有被入栈函数体的地址和值。有点像是goto语句的标记tag或lab,在入栈的时候做了个标记一样。 函数体出栈的时候,会得到出栈函数体的地址和值。有点像goto语句跳到之前做好的标记一样。 这张图表示的是图的深度遍历的时候,递归栈是怎么运作的,拿来解释二叉树遍历的递归栈运作道理是一样的。 递归的时候本身系统会自动分配管理堆栈。 如果写成迭代就要自己分配出栈和入栈。
游客886 2019-12-02 01:25:12 0 浏览量 回答数 0

回答

getCurrentPages() 方法用于获取当前页面栈的实例,返回页面数组栈。第一个 元素为首页,最后一个元素为当前页面。 框架以栈的形式维护当前的所有页面。路由切换与页面栈的关系如下: 路由方式 页面栈表现 初始化 新页面入栈 打开新页面 新页面入栈 页面重定向 当前页面出栈,新页面入栈 页面返回 当前页面出栈 Tab 切换 页面全部出栈,只留下新的 Tab 页面 下面代码可以用于检测当前页面栈是否具有 5 层页面深度 my.redirectTo({ url: '/pages/logs/logs' }); } else { my.navigateTo({ url: '/pages/index/index' }); } 注意: 不要尝试修改页面栈,会导致路由以及页面状态错误。 内容来源:https://developer.aliyun.com/article/756818?spm=a2c6h.12873581.0.dArticle756818.26162b70Su1GZy&groupCode=tech_library
KaFei 2020-04-27 14:27:01 0 浏览量 回答数 0

回答

补充说明:栈,主要存放引用和基本数据类型;堆,用来存放new 出来的对象实例,包括对象变量以及对象方法。java中很少自己操作堆栈,除非对性能有严格要求的情况下,例如netty的内存管理。但深刻理解堆栈的区别,有助于写出更优秀的程序。栈是跟着线程的,每个线程有一个栈。而堆只有一个,一个jvm只有一个堆内存。栈也是线程私有的,生命周期与线程相同。java中广义的栈,指的就是此处内存。每个方法在执行时,都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法调用到执行完毕,都对应一个栈帧在虚拟机栈中入栈出栈的过程。
sz0729 2019-12-02 02:39:52 0 浏览量 回答数 0

回答

每个线程都有一个JVM栈,并跟随线程的启动而创建。其中存储的数据无素称为栈帧(Stack Frame)。JVM会每把栈桢压入JVM栈或从中弹出一个栈帧。如果有任何异常抛出,像printStackTrace()方法输出的栈跟踪信息的每一行表示一个栈帧
游客pklijor6gytpx 2019-12-23 14:57:03 0 浏览量 回答数 0

回答

用栈不是很好解决吗?维护两个栈或一个栈,一个数组,先将字符入栈,然后判断栈顶与栈顶的下一个元素是否相同,是就出栈,数组里面的最后一个元素自加一;否则栈不动,数组下一个地址赋值1。最后输出栈内元素与数组就好了
a123456678 2019-12-02 02:16:02 0 浏览量 回答数 0

问题

C++用类对堆栈的操作,请写详细点

class STACK{int *const elems; //申请内存用于存放栈的元素const int max; //栈能存放的最大元素个数int pos; //栈实际已有元素个数,栈空时pos=0;public:STACK(int m...
a123456678 2019-12-01 20:07:30 1052 浏览量 回答数 1

问题

Java 实例 - 压栈出栈的方法实现字符串反转

Java 实例 - 压栈出栈的方法实现字符串反转...
问问小秘 2020-02-13 17:03:27 4 浏览量 回答数 1

回答

(1)不需要,因为它永远是最先访问,相当于入栈后立刻就弹出了。所以不需要(2)需要将当前节点两个子节点入栈,每次出栈的时候再压入它的子节点。(3)if (top>0) bt=s[top--]出栈。取出top位置的元素,并且堆栈往后收缩一个。(4) 中序需要将父节点入栈后序不需要堆栈
51干警网 2019-12-02 01:34:50 0 浏览量 回答数 0

回答

System.out.println("元素"+stack.pop()+"出栈");这句话本身会让栈顶出栈换成System.out.println("元素"+stack.peek()+"出栈");
蛮大人123 2019-12-02 02:38:46 0 浏览量 回答数 0

回答

您的程序中存在如下问题 if (r = 0) //这是赋值,而非条件判断,特别注意 cout << "Yes\n"; else cout << "No\n"; 下面的代码是根据题意重新编写的,基本思路是给定的出栈顺序与原顺序相比,要么下一个出栈的数没有在栈中出现过,要么出现就是在栈顶的位置,如果这两个情况都不满足,则该顺序不可能为正确的出栈顺序。 #include <cstdio> #include using namespace std; int isCorrectOrder(int * arr, int len) { int i, j; int stack[10], cur, index; for(i = 0, cur = 1, index = 0; i < len; ++ i) { if(arr[i] >= cur) //下一个要出栈的数没有在栈中出现过 { for(j = cur; j < arr[i]; ++ j) stack[index ++] = j; cur = arr[i] + 1; } else { if(stack[index-1] == arr[i]) //下一个出栈的数在栈顶位置 -- index; else //其他情况则不可能是给定已经顺序的出栈顺序 return -1; } } return 0; } int main() { int T, ret; int n[12]; cin >> T; while (T--) { for (int i = 0; i < 10; i++) cin >> n[i]; ret = isCorrectOrder(n, 10); if (ret == 0) cout << "Yes\n"; else cout << "No\n"; } system("pause"); return 0; }
a123456678 2019-12-02 01:57:17 0 浏览量 回答数 0

问题

四个栈排序问题

问题是这样的,要求用户输入1到n个数.这个数列必须是连续的,也就是1,2,3,4....n这样的,现在有四个栈,三个栈作为辅助栈,有一个栈看作是输出栈。目标是要把输入的数列排序好,进入输出栈中,最终令到输出栈一个个pop出来是9 8 7 6...
蛮大人123 2019-12-01 20:17:06 975 浏览量 回答数 1

回答

递归是一种程序设计的方式和思想。计算机在执行递归程序时,是通过栈的调用来实现的。 栈,从抽象层面上看,是一种线性的数据结构,这中结构的特点是“先进后出”,即假设有a, b,c三个元素,依次放某个栈式存储空间中,要从该空间中拿到这些元素,那么只能以c、b、a的顺序得到。 递归程序是将复杂问题分解为一系列简单的问题,从要解的问题起,逐步分解,并将每步分解得到的问题放入“栈”中,这样栈顶是最后分解得到的最简单的问题, 解决了这个问题后,次简单的问题可以得到答案,以此类推。 分解问题是进栈(或者说压栈)的过程,解决问题是一个出栈的过程。
行者武松 2019-12-02 01:24:17 0 浏览量 回答数 0

回答

可以啊,通过PUSH压栈和PULL出栈就可以例子很多,而且在底层的话也就是通过压栈和出栈来实现递归算法的~
寒凝雪 2019-12-02 01:24:58 0 浏览量 回答数 0

回答

用内存角度来解释的话,在JVM的方法栈和堆内存这两个内存中,当运行Object obj = new Object();时,在方法栈的栈顶中放入Object obj,然后在堆中生成一个Object对象,最后方法栈中的obj指向Object对象,这时完成一次循环的内存动作。接着第二次循环, new Object();又产生一个新的Object对象,然后方法栈中的obj指向新的Object对象,第一次循环的Object对象没有被任务变量引用,成为了垃圾,等JVM垃圾回收器回收。其它循环以此类推了。在这里我提一下题外话,JVM有个优秀的算法是,JVM的方法栈,一个方法的调用产生一个栈帧,这个栈帧在方法调用时计算出你有几个局部变量,这个计算会过滤作用域的,如你的循环后面再建立一个局部变量,那JVM认为只有一个局部变量,因为你的Object obj = new Object();在大括号内,出了大括号就没有了,所以只需要一个位置来存放局部变量即可,也就是说运行到大括号时用这个位置来执行代码,出了大括号后下一个局部变量就可以接着使用这个栈的这个位置来执行下面的代码了。
蛮大人123 2019-12-02 02:16:43 0 浏览量 回答数 0

问题

为什么二叉树的前序遍历和中序遍历对应入栈和出栈次序

为什么二叉树的前序遍历和中序遍历对应入栈和出栈次序...
知与谁同 2019-12-01 20:16:09 1024 浏览量 回答数 1

回答

为运算符设置一个栈存储,左括号直接压入栈顶,遇到右括号则依次弹出操作符栈参与运算,数字也是一个栈,直到弹出的是左括号
1837040409601280 2019-12-02 01:00:12 0 浏览量 回答数 0

回答

此处延伸:栈(First In Last Out)与队列(First In First Out)的区别 栈与队列的区别: 队列先进先出,栈先进后出 2. 对插入和删除操作的"限定"。 栈是限定只能在表的一端进行插入和删除操作的线性表。 队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。 遍历数据速度不同 standard 模式 这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中。使用场景:大多数Activity。 singleTop 模式 如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent() ),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。使用场景如新闻类或者阅读类App的内容页面。 singleTask 模式 如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNewIntent() )。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。使用场景如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。 singleInstance 模式 在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent() )。其效果相当于多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。使用场景如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。
剑曼红尘 2020-04-10 12:41:17 0 浏览量 回答数 0

回答

栈是后进先出,只要记住这一点,就不难实现。下面是我的实现: /** * StackByArray:数组方式实现栈,实现栈的初始化,入栈出栈操作 * * @author zeng.xiangdong 1770534116@qq.com * @version V1.0 2014-8-24 下午10:00:03 */ public class StackByArray { // 栈的长度 private int length; // 栈 private Object[] array; // 栈顶的下标 private int topIndex = -1; /** * StackByArry:构造方法 * * @param length 初始化栈的长度 */ public StackByArray(int length) { this.length = length; array = new Object[length]; } /** * push:入栈 * * @param obj * @throws Exception */ private void push(Object obj) throws Exception { if(isFullStack()) { throw new Exception("栈已满"); } array[++topIndex] = obj; } /** * offer: 出栈,取出栈顶元素 * * @return obj * @throws Exception */ private Object offer() throws Exception { if(isEmptyStack()) { throw new Exception("栈已空"); } Object topObject = array[topIndex]; array[topIndex] = null; topIndex--; return topObject; } /** * isEmptyStack:判断栈是否为空 * * @return */ private boolean isEmptyStack() { return (topIndex == -1); } /** * isFullStack:判断栈是否已满 * * @return */ private boolean isFullStack() { return topIndex == (length - 1); } /** * getLength:取得栈内的数据长度 * * @return length */ private int getLength() { return topIndex + 1; } /** * outPut:栈内容输出 */ private void outPut() { for(Object obj : array) { if(obj != null) { System.out.print(obj + ", "); } } System.out.println(); } /** * main:(这里用一句话描述这个方法的作用) * * @param args */ public static void main(String[] args) throws Exception { StackByArray stack = new StackByArray(10); for(int i = 1; i <= 8; i++) { stack.push(i); stack.outPut(); } System.out.println("---------------"); System.out.println(stack.offer()); stack.outPut(); System.out.println("---------------"); System.out.println(stack.offer()); stack.outPut(); System.out.println(stack.getLength()); } }
蛮大人123 2019-12-02 02:15:06 0 浏览量 回答数 0

问题

有n个字符的字符串,判断字符串是否回文

有n个字符的字符串,判断字符串是否回文。 如: abcba和abccba都是回文;实验要求:1.将输入的字符串放在单链表栈中;2. 利用栈的入栈和出栈完成是否回文判断;3. 应提供栈的基本操作,如栈的初始化、销毁、判断是否栈空等;怎么用c+...
a123456678 2019-12-01 20:07:22 977 浏览量 回答数 1

回答

栈内存一般而言由系统的约束和配置决定,一般来说默认的在1-8M每进程不等。linux和macosx一般可以用ulimit命令调节,但一般仍会受到操作系统的“硬限制”而不能想调多大调多大。 关于系统栈: 系统栈的目的,在于跟踪和追溯函数调用的历史。这里的函数尤其指每个函数都有功能上的意义,即打印出系统栈就能表示出程序有意义的功能分块。使用栈内存做算法,不是不好,不过和系统栈的惯例用法毕竟稍微有一点偏差。层级很深,但每个层级都很类似(单独提取出来,无甚分析价值)的深层搜索,无论是为了防止炸栈,还是为了保持调用栈的简洁可分析,都应当自己建立用户栈。关于栈内存: 栈内存整批进、整批退,不存在碎片和管理问题,性能优于堆内存。但相应的代价就是: 其一,比较少,寸土寸金;其二,函数结束(退栈)之后,栈顶退回函数执行前的位置,则函数中所有局部变量所在的栈内存落在栈外自然销毁。(重要)因此函数内的局部变量,只应当表示函数内部执行到了什么状态,绝对不能用作实质性的数据区域。例如:应当用局部变量表示缓冲区的下标、指针,但缓冲区本身绝对不应该开在栈上。数据区域要么从堆上分配获得指针,要么通过参数传递得到指针。事实上这里用全局变量也不是唯一的选择(至少全局变量在语义上还是不太好看)。也可以使用malloc()来做,获取按需分配内存的灵活性,不必事前规定一个“足够大的范围”: int n, i;int a, p;scanf("%d", &n);a = malloc(sizeof(int) * n);p = a;for (i=0; i{ scanf("%d", p++); }题外话——“局部变量所在的栈内存区域自然销毁”这件事,坑死多少C程序员。这里不详细展开,但请一定小心不要撞这个常识性的错误,即:不要把任何局部变量的地址用return返回给调用者。
a123456678 2019-12-02 02:39:20 0 浏览量 回答数 0

回答

栈帧:在JVM中一旦有方法执行,JVM就会为之创建一个栈帧,并把其添加到当前线程的JVM栈中。当方法运行结束时,栈帧也会相应的从JVM栈中移除。栈帧中存放着对本地变量数组、操作数栈以及属于当前运行方法的运行时常量池的引用。本地变量数组和操作数栈的大小在编译时就已确定,所以属在运行时属于方法的栈帧大小是固定的。 本地变量数组:本地变量数组的索引从0开始计数,其位置存储着对方法所属类实例的引用。从索引位置1开始的保存的是传递给该方法的参数。其后存储的就是真正的方法的本地变量了。 操作数栈:是方法的实际运行空间。每个方法变换操作数栈和本地变量数组,并把调用其它方法的结果从栈中弹或压入。在编译时,编译器就能计算出操作数栈所需的内存窨,因此操作数栈的大小在编译时也是确定的。
游客pklijor6gytpx 2019-12-23 14:57:42 0 浏览量 回答数 0

回答

我们都知道虚拟机的内存划分了多个区域,并不是一张大饼。那么为什么要划分为多块区域呢,直接搞一块区域,所有用到内存的地方都往这块区域里扔不就行了,岂不痛快。是的,如果不进行区域划分,扔的时候确实痛快,可用的时候再去找怎么办呢,这就引入了第一个问题,分类管理,类似于衣柜,系统磁盘等等,为了方便查找,我们会进行分区分类。另外如果不进行分区,内存用尽了怎么办呢?这里就引入了内存划分的第二个原因,就是为了方便内存的回收。如果不分,回收内存需要全部内存扫描,那就慢死了,内存根据不同的使用功能分成不同的区域,那么内存回收也就可以根据每个区域的特定进行回收,比如像栈内存中的栈帧,随着方法的执行栈帧进栈,方法执行完毕就出栈了,而对于像堆内存的回收就需要使用经典的回收算法来进行回收了,所以看起来分类这么麻烦,其实是大有好处的。 提到虚拟机的内存结构,可能首先想起来的就是堆栈。对象分配到堆上,栈上用来分配对象的引用以及一些基本数据类型相关的值。但是·虚拟机的内存结构远比此要复杂的多。除了我们所认识的(还没有认识完全)的堆栈以外,还有程序计数器,本地方法栈和方法区。我们平时所说的栈内存,一般是指的栈内存中的局部变量表。 从图中可以看到有5大内存区域,按照是否被线程所共享可分为两部分,一部分是线程独占区域,包括Java栈,本地方法栈和程序计数器。还有一部分是被线程所共享的,包括方法区和堆。什么是线程共享和线程独占呢,非常好理解,我们知道每一个Java进行都会有多个线程同时运行,那么线程共享区的这片区域就是被所有线程一起使用的,不管有多少个线程,这片空间始终就这一个。而线程的独占区,是每个线程都有这么一份内存空间,每个线程的这片空间都是独有的,有多少个线程就有多少个这么个空间。上图的区域的大小并不代表实际内存区域的大小,实际运行过程中,内存区域的大小也是可以动态调整的。下面来具体说说每一个区域的主要功能。 程序计数器,我们在写代码的过程中,开发工具一般都会给我们标注行号方便查看和阅读代码。那么在程序在运行过程中也有一个类似的行号方便虚拟机的执行,就是程序计数器,在c语言中,我们知道会有一个goto语句,其实就是跳转到了指定的行,这个行号就是程序计数器。存储的就是程序下一条所执行的指令。这部分区域是线程所独享的区域,我们知道线程是一个顺序执行流,每个线程都有自己的执行顺序,如果所有线程共用一个程序计数器,那么程序执行肯定就会出乱子。为了保证每个线程的执行顺序,所以程序计数器是被单个线程所独显的。程序计数器这块内存区域是唯一一个在jvm规范中没有规定内存溢出的。 java虚拟机栈,java虚拟机栈是程序运行的动态区域,每个方法的执行都伴随着栈帧的入栈和出栈。 栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。栈帧中包括了局部变量表,操作数栈,方法返回地址以及额外的一些附加信息,在编译过程中,局部变量表的大小已经确定,操作数栈深度也已经确定,因此栈帧在运行的过程中需要分配多大的内存是固定的,不受运行时影响。对于没有逃逸的对象也会在栈上分配内存,对象的大小其实在运行时也是确定的,因此即使出现了栈上内存分配,也不会导致栈帧改变大小。 一个线程中,可能调用链会很长,很多方法都同时处于执行状态。对于执行引擎来讲,活动线程中,只有栈顶的栈帧是最有效的,称为当前栈帧,这个栈帧所关联的方法称为当前方法。执行引擎所运行的字节码指令仅对当前栈帧进行操作。Ft5rk58GfiJxcdcCzGeAt8fjkFPkMRdf 局部变量表:我们平时所说的栈内存一般就是指栈内存中的局部变量表。这里主要是存储变量所用。对于基本数据类型直接存储其值,对于引用数据类型则存储其地址。局部变量表的最小存储单位是Slot,每个Slot都能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据。 既然前面提到了数据类型,在此顺便说一下,一个Slot可以存放一个32位以内的数据类型,Java中占用32位以内的数据类型有boolean、byte、char、short、int、float、reference和returnAddress八种类型。前面六种不需要多解释,大家都认识,而后面的reference是对象的引用。虚拟机规范既没有说明它的长度,也没有明确指出这个引用应有怎样的结构,但是一般来说,虚拟机实现至少都应当能从此引用中直接或间接地查找到对象在Java堆中的起始地址索引和方法区中的对象类型数据。而returnAddress是为字节码指令jsr、jsr_w和ret服务的,它指向了一条字节码指令的地址。 对于64位的数据类型,虚拟机会以高位在前的方式为其分配两个连续的Slot空间。Java语言中明确规定的64位的数据类型只有long和double两种(reference类型则可能是32位也可能是64位)。值得一提的是,这里把long和double数据类型读写分割为两次32读写的做法类似。不过,由于局部变量表建立在线程的堆栈上,是线程私有的数据,无论读写两个连续的Slot是否是原子操作,都不会引起数据安全问题。 操作数栈是一个后入先出(Last In First Out, LIFO)栈。同局部变量表一样,操作数栈的最大深度也在编译的时候被写入到字节码文件中,关于字节码文件,后面我会具体的来描述。操作数栈的每一个元素可以是任意的Java数据类型,包括long和double。32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2。在方法执行的任何时候,操作数栈的深度都不会超过在max_stacks数据项中设定的最大值。 当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令向操作数栈中写入和提取内容,也就是入栈出栈操作。例如,在做算术运算的时候是通过操作数栈来进行的,又或者在调用其他方法的时候是通过操作数栈来进行参数传递的。 举个例子,整数加法的字节码指令iadd在运行的时候要求操作数栈中最接近栈顶的两个元素已经存入了两个int型的数值,当执行这个指令时,会将这两个int值和并相加,然后将相加的结果入栈。 操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,在编译程序代码的时候,编译器要严格保证这一点,在类校验阶段的数据流分析中还要再次验证这一点。再以上面的iadd指令为例,这个指令用于整型数加法,它在执行时,最接近栈顶的两个元素的数据类型必须为int型,不能出现一个long和一个float使用iadd命令相加的情况。 本地方法栈 与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。 方法区经常会被人称之为永久代,但这俩并不是一个概念。首先永久代的概念仅仅在HotSpot虚拟机中存在,不幸的是,在jdk8中,Hotspot去掉了永久代这一说法,使用了Native Memory,也就是Metaspace空间。那么方法区是干嘛的呢?我们可以这么理解,我们要运行Java代码,首先需要编译,然后才能运行。在运行的过程中,我们知道首先需要加载字节码文件。也就是说要把字节码文件加载到内存中。好了,问题就来了,字节码文件放到内存中的什么地方呢,就是方法区中。当然除了编译后的字节码之外,方法区中还会存放常量,静态变量以及及时编译器编译后的代码等数据。 堆,一般来讲堆内存是Java虚拟机中最大的一块内存区域,同方法区一样,是被所有线程所共享的区域。此区域所存在的唯一目的就存放对象的实例(对象实例并不一定全部在堆中创建)。堆内存是垃圾收集器主要光顾的区域,一般来讲根据使用的垃圾收集器的不同,堆中还会划分为一些区域,比如新生代和老年代。新生代还可以再划分为Eden,Survivor等区域。另外为了性能和安全性的角度,在堆中还会为线程划分单独的区域,称之为线程分配缓冲区。更细致的划分是为了让垃圾收集器能够更高效的工作,提高垃圾收集的效率。 如果想要了解更多的关于虚拟机的内容,可以观看录制的<深入理解Java虚拟机>这套视频教程。
zwt9000 2019-12-02 00:21:07 0 浏览量 回答数 0

云产品推荐

上海奇点人才服务相关的云产品 小程序定制 上海微企信息技术相关的云产品 国内短信套餐包 ECS云服务器安全配置相关的云产品 开发者问答 阿里云建站 自然场景识别相关的云产品 万网 小程序开发制作 视频内容分析 视频集锦 代理记账服务 阿里云AIoT