可以直接用输出在函数内部调用时,把调用顺序打印出来。
一、一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其 *** 作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
二、例程:
//maincppint a = 0; 全局初始化区
char p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char p2; 栈
char p3 = "123456"; 123456在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char )malloc(10);
p2 = (char )malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
callstack是函数调用堆栈,就是程序运行时函数的调用过程,例如A函数调用了B函数,那么程序执行到B函数的时候,callstack里就会有A函数,因为函数调用时需要把当前函数入栈,在B函数执行完毕后再从堆栈里将A函数取出,以让程序指针回到A函数继
程序中,一个函数是一个过程,这个过程可以分为包括传入参数、过程代码、返回三部分构成。由于一个函数过程需要用到内部变量、临时变量等,所以需要在进程空间的栈空间分配一段存储片段来存储函数过程中的这些参数,该内存片段即为栈帧。
栈帧的由来:
为一个函数的过程提供一个存储函数局部变量,参数,返回地址和其他临时变量;
栈帧的周期:
进入函数~函数返回,该阶段内栈帧作为
不同的语言具体的实现方式略有不同,但是,总体上,fun(a,b);
局部变量:
包括函数传入的形参和函数内部定义的变量;
返回地址:
指令指针p指向call
fun,那么fun栈帧存储的返回地址为p+1;现今的编译器的一个约定是将返回地址存到一个固定的寄存器中,这样比读取栈帧(内存)效率要高。
临时变量:
主要为计算,运算过程中的中间临时变量;
参数传递:
其一:如果fun中调用另一个函数k(a,bn);那么,传递的参数是形参,按照现代编译规定,前k个形参是通过寄存器传递,后n-k个形参通过栈帧的实参部分(栈帧的尾部)来传递;
其二:主要为在fun中要调用函数g(&a,&b),那么为g()函数传出实参(形参是通过寄存器来传递的)是通过“传出实参”区块进行的,这么做主要是为了保证该实参能够被内层嵌套的函数访问。比如,g函数由调用一个k(a地址)函数,同样需要用到a的地址,所以fun在传递参数时必须为实参(&a)传递申请固定的内存存储空间(而非用寄存器)这样k函数可以通过固定的内存地址(fun的形参列表来获取参数值)。这时的g的栈帧为fun栈帧的下一帧(相邻的空间地址),即调用者和被调用者的栈帧是相连的;
保护的寄存器:
栈帧作为函数过程的一个临时内存存储区块,同时负责函数调用过程中寄存器值的保存和还原。即:假设fun函数目前占用了寄存器ri存储一个临时变量t,而此时调用了函数g(),在g()函数中可能需要用到寄存器ri做运算的临时存储,那么如何确保g()函数调用返回后,ri恢复到fun中t的原来值。这就需要在调用者或者被调用者中选择其一来保存原有ri的值,即caller-save或者callee-save。
无论是否递归调用,当在一个函数(外层函数)的运行期间调用另一个函数(被调用函数,即内层函数)时,在运行被调用函数之前,系统需要先完成3个 *** 作,即:
从被调函数返回调用函数(外层函数)之前,系统还要完成3个 *** 作,即:
当有多个函数构成嵌套调用时,按照"后调用先返回"的原则,上述函数之间的信息传递和控制转移必须通过"栈"来实现,每当调用一个函数时,就在栈顶为它分配一个存储区,每当退出一个函数时,就释放它的存储区,当前正在运行的函数的数据区必在栈顶。 递归函数 的运行过程类似于多个函数的嵌套调用,只是调用和被调用函数是同一个函数。
函数调用时,需要在栈中分配新的帧,将 返回地址 , 调用参数 和 局部变量 入栈。所以递归调用越深,占用的栈空间越多。
为了解决递归的开销大问题,使用尾递归优化,具体分两步:
使用尾递归的好处:因为进入下一层函数不再需要参考外层函数的信息,因此没必要保存外层函数的栈信息,递归需要用的stack只有目前这层被调用函数的,因此避免了栈溢出风险。
一些语言提供了尾递归优化特性,当识别出使用了尾递归时,会相应地只保留当前层函数的stack,节省内存,不会发生stackOverflowException调用栈溢出。
注意:
JVM缺乏尾调用指令,java编译器对尾递归的优化未实现,所以在java中尽量避免过深的递归调用,如果需要使用递归,建议优化成迭代式。
尾递归就是 *** 作的最后一步是调用自身的递归。注意,尾递归的判断标准是函数运行 最后一步 是否调用自身,而不是是否在函数的最后一行调用自身。
这个不是尾递归:
这个是尾递归:
参考:
[1] 什么是尾递归
[2] 尾调用优化
[3] 栈是如何实现递归的:递归与栈一些不得不说的事
[4] stack 的三种含义
首先你需要确定,你想说哪方面的编程
最普通的有C语言(有点老了),但是作为一种编程基础还是非常重要的
如果有需要要学C语言,建议谭浩强的书都还不错
除了C语言就是C++(C Plus Plus 简称CPP)
这个是面向对象编程的使用较多的语言了,很实用
因为VC编程环境没有所谓的中文版,所以学习还是有一定难度的
可以考虑在很多论坛编程论坛看看,比如CSDN
当然,最近新兴的编程语言莫过于Java了,跨平台适用性最强的就是这个了。有关Java学习的书实在太多了,建议你去书店看看,找那些你个人能够看得明白并且看着舒服的教材。
除了以上这些传统的编程,还有很多网络编程也是很流行的
譬如PHP、ASP、JSP等等
其中每一样学好了都是可以独当一面的,这个需要看你个人的兴趣以及发展的方向
学习的方法有很多
网络上各种论坛,好多IT网站也有很多的教学教程
推荐你一个可以下载电子书的网站
里面的书很全,很不错
关于你所提到的编程的大院校,我还是第一次听说
这个我是否可以理解为那种电脑学校啊
我只能说,学习还是要靠自己
不能只是一时的冲动,而是一种坚持的过程!
当然,有一个好的老师指引将是一件非常完美的事情
学编程不在于是否会英语
其实,编程所用的并不是英语
而只是用英语作为一种标记而已
学会其中的语法和算法的精髓才是重点!
当然学好英语对理解各种语法和算法还是有帮助的
建议你还是要好好学习和涉猎一些
PS:
无论学习哪种编程,学习数据结构都是一个必要的环节
还是建议你看看英文原著的那些数据结构教材
中译本也是可以的
最后就是祝你好运!
有问题,可以继续补充问题
以上就是关于c语言如何将函数调用堆栈打印出来全部的内容,包括:c语言如何将函数调用堆栈打印出来、怎么查看call02stack即"调用堆栈、C语言栈的调用。等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)