堆栈寻址的原理桢的生成原理(调试器是如何生成堆栈寻址的原理的调

如何确定程序在运行(非gdb调试中)时的栈桢%ebp及(%ebp)值?【c语言吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:586,205贴子:
如何确定程序在运行(非gdb调试中)时的栈桢%ebp及(%ebp)值?收藏
程序checkebp.c如下,用gcc编译成目标文件checkebp,运行环境为redhat&linux&9
根据程序,其运行时的栈桢结构应该是一样的,那末我多次运行程序:当每次运行到过程testebp()中时,其栈桢指针%ebp和内存(%ebp)的内容应该是不变的。
可是当我用gdb调试目标文件时,调试两次(见下注释),却发现在gdb中看到的%ebp、(%ebp)在两次中是不一样的,这是为什莫?
那末我应该如何确定程序在运行(非gdb下调试)时,过程testebp()的栈桢指针%ebp和内存(%ebp)的值。
--------------checkebp.c------------------------------------------------------------------------------
#include&&stdio.h&
void&testebp()
{
&&char&i=9;
&&printf(&i=%x\n&,i);
}
void&main()
{
&&testebp();
}
--------------第一次用gdb调试------------------------------------------------------------------------------------------
ot@redhat9-1&test]#&gdb&checkebp
GNU&gdb&Red&Hat&Linux&(5.3post-0.rh)
Copyright&2003&Free&Software&Foundation,&Inc.
GDB&is&free&software,&covered&by&the&GNU&General&Public&License,&and&you&are
welcome&to&change&it&and/or&distribute&copies&of&it&under&certain&conditions.
Type&&show&copying&&to&see&the&conditions.
There&is&absolutely&no&warranty&for&GDB.&&Type&&show&warranty&&for&details.
This&GDB&was&configured&as&&i386-redhat-linux-gnu&...
(gdb)&disas&main
Dump&of&assembler&code&for&function&main:
0x&&main+0&:&&&&push&&&%ebp
0x0804834a&&main+1&:&&&&mov&&&&%esp,%ebp
0x0804834c&&main+3&:&&&&sub&&&&$0x8,%esp
0x0804834f&&main+6&:&&&&and&&&&$0xfffffff0,%esp
0x&&main+9&:&&&&mov&&&&$0x0,%eax
0x&&main+14&:&&&sub&&&&%eax,%esp
0x&&main+16&:&&&call&&&0x8048328&&testebp&
0x0804835e&&main+21&:&&&leave
0x0804835f&&main+22&:&&&ret
End&of&assembler&dump.
(gdb)&break&*0x8048359
Breakpoint&1&at&0x8048359
(gdb)&run
Starting&program:&/root/test/checkebp
&
Breakpoint&1,&0x&in&main&()
(gdb)&si
0x&in&testebp&()
(gdb)&disas&testebp
Dump&of&assembler&code&for&function&testebp:
0x&&testebp+0&:&push&&&%ebp
0x&&testebp+1&:&mov&&&&%esp,%ebp
0x0804832b&&testebp+3&:&sub&&&&$0x8,%esp
0x0804832e&&testebp+6&:&movb&&&$0x9,0xffffffff(%ebp)
0x&&testebp+10&:&&&&&&&&sub&&&&$0x8,%esp
0x&&testebp+13&:&&&&&&&&movsbl&0xffffffff(%ebp),%eax
0x&&testebp+17&:&&&&&&&&push&&&%eax
0x0804833a&&testebp+18&:&&&&&&&&push&&&$0x804840c
0x0804833f&&testebp+23&:&&&&&&&&call&&&0x8048268&&printf&
0x&&testebp+28&:&&&&&&&&add&&&&$0x10,%esp
0x&&testebp+31&:&&&&&&&&leave
0x&&testebp+32&:&&&&&&&&ret
End&of&assembler&dump.
(gdb)&si
0x&in&testebp&()
(gdb)&si
0x0804832b&in&testebp&()
(gdb)&x&/x&$ebp&&&&&&&&&&&&&&&&//第一次观察%ebp和(%ebp)
0xbfffde08:&&&&&0xbfffde18
(gdb)&continue
Continuing.
i=9
&
Program&exited&with&code&04.
(gdb)&quit
[root@redhat9-1&test]#
---------------------第二次用gdb调试-----------------------------------------------------------------
[root@redhat9-1&test]#&gdb&checkebp
GNU&gdb&Red&Hat&Linux&(5.3post-0.rh)
Copyright&2003&Free&Software&Foundation,&Inc.
GDB&is&free&software,&covered&by&the&GNU&General&Public&License,&and&you&are
welcome&to&change&it&and/or&distribute&copies&of&it&under&certain&conditions.
Type&&show&copying&&to&see&the&conditions.
There&is&absolutely&no&warranty&for&GDB.&&Type&&show&warranty&&for&details.
This&GDB&was&configured&as&&i386-redhat-linux-gnu&...
(gdb)&disas&main
Dump&of&assembler&code&for&function&main:
0x&&main+0&:&&&&push&&&%ebp
0x0804834a&&main+1&:&&&&mov&&&&%esp,%ebp
0x0804834c&&main+3&:&&&&sub&&&&$0x8,%esp
0x0804834f&&main+6&:&&&&and&&&&$0xfffffff0,%esp
0x&&main+9&:&&&&mov&&&&$0x0,%eax
0x&&main+14&:&&&sub&&&&%eax,%esp
0x&&main+16&:&&&call&&&0x8048328&&testebp&
0x0804835e&&main+21&:&&&leave
0x0804835f&&main+22&:&&&ret
End&of&assembler&dump.
(gdb)&break&*0x8048359
Breakpoint&1&at&0x8048359
(gdb)&run
Starting&program:&/root/test/checkebp
&
Breakpoint&1,&0x&in&main&()
(gdb)&si
0x&in&testebp&()
(gdb)&disas&testebp
Dump&of&assembler&code&for&function&testebp:
0x&&testebp+0&:&push&&&%ebp
0x&&testebp+1&:&mov&&&&%esp,%ebp
0x0804832b&&testebp+3&:&sub&&&&$0x8,%esp
0x0804832e&&testebp+6&:&movb&&&$0x9,0xffffffff(%ebp)
0x&&testebp+10&:&&&&&&&&sub&&&&$0x8,%esp
0x&&testebp+13&:&&&&&&&&movsbl&0xffffffff(%ebp),%eax
0x&&testebp+17&:&&&&&&&&push&&&%eax
0x0804833a&&testebp+18&:&&&&&&&&push&&&$0x804840c
0x0804833f&&testebp+23&:&&&&&&&&call&&&0x8048268&&printf&
0x&&testebp+28&:&&&&&&&&add&&&&$0x10,%esp
0x&&testebp+31&:&&&&&&&&leave
0x&&testebp+32&:&&&&&&&&ret
End&of&assembler&dump.
(gdb)&si
0x&in&testebp&()
(gdb)&si
0x0804832b&in&testebp&()
(gdb)&x/x&$ebp&&&&&&&&&&&&&//第二次观察%ebp和(%ebp)
0xbfffe408:&&&&&0xbfffe418
(gdb)&x/x&$ebp+4
0xbfffe40c:&&&&&0x0804835e
(gdb)&continue
Continuing.
i=9
&
Program&exited&with&code&04.
(gdb)&quit
[root@redhat9-1&test]#
快试试吧,可以对自己使用挽尊卡咯~◆◆
程序每次运行时操作系统分配的进程空间都不会一样,栈地址也会随之变化吧。
登录百度帐号51CTO旗下网站
如何调试Android Framework?
如果你仔细看完了本文和我给出的链接,那么应该对Debug技术不再陌生了;接下来你可以选择Framework层的代码,手动调试一下加深理解;在日后的工作过程中,不断滴加强debug技术的练习,让它称为你解决复杂问题的条件反射,一定会事半功倍!
作者:佚名来源:| 12:59
Linus有一句名言广为人知:Read the fucking source code.
但其实,要深入理解某个软件、框架或者系统的工作原理,仅仅「看」代码是远远不够的。就拿Android
Framework来说,整个代码量非常大不说,那些个动辄几万行的类如何去理解?所以我今天要说的就是:
Debug the fucking source code!!
之前分享过一个答案:大家遇到过什么 Android
兼容性问题?,这里面的有一些非常诡异的问题,我相信光靠看代码你是永远定位不出来的。还有我写的一系列Android插件框架原理的文章,这里面涉及到大量Android
Framework层的知识,有小伙伴会问,这些Framework层的原理,你是如何学习的呢,有诀窍吗?有!那就是调试。
Debug是一项非常非常重要的技能,毋庸多言。今天我就给大家分享一下「调试Android
Framework」的经验,一旦掌握这项技能,那么Java层的任何问题都拦不住你了。
其实整个调试过程非常简单:
在你要调试进程的合适位置打上断点
跟踪代码(Step in/out/over等等)
在展开讲述这两方面之前,有必要先简单了解下调试的基础知识。Java平台的调试是有一个规范化的标准的,那就是JPDA(Java Platform
Debugger Architecture);通过 JPDA 提供的 API,开发人员可以方便灵活的搭建 Java 调试应用程序。 JPDA
主要由三个部分组成:Java 虚拟机工具接口(JVMTI),Java 调试线协议(JDWP),以及 Java 调试接口(JDI)。
Java程序的调试无非就是通过一个调试器(debugger)获取对应Java虚拟机的信息,上文所述的JDWP就是调试器与虚拟机通信的桥梁。在dalvik虚拟机内部有一个专门的jdwp线程,Android系统的adbd进程通过socket与各个虚拟机的jdwp线程进行通信,外部调试器通过adb工具与adbd通信进而完成与jdwp的通信。我们通常所说的「attach
debugger」指的就是这个意思&&连接到指定的需要调试的进程。
调试器工作原理
如何在正确的地方下断点
「正确的地方」包含两个含义:首先,调试是以进程为单位进行的,如果你需要调试运行在进程A 中的代码,却把debugger
attach到了B进程,那么这个断点压根儿就是牛头不对马嘴;另外呢,比如你想调试Android的多媒体框架,你得知道media相关的类在哪吧,也就是说需要在正确的函数里面下断点。
如何在合适的进程下断点?
如果是调试我们自己写的App,在Android Studio里面非常简单,在Run菜单de最后面有一个attach debugger to android
process 的选项,点击之后会出现一个菜单,选择自己需要调试的进程即可;但是,如果需要调试Android
Framework层的代码,这样做是达不到目的的&&Framework层的代码通常运行在别的进程(比如ActivityManagerService运行在system_server进程),而这些进程通常情况下是不可调试的,也就是说在attach
debugger to android process 的那个菜单里面不会有系统的进程,如下图:
普通的无法调试的Android设备
为什么不可调试呢?上文我们简要讲述了调试器的工作原理,我们知道每一个虚拟机有一个jdwp线程,如果这个线程拒绝连接到调试器,你也就没办法对这个进程进行调试了。Android的所有App进程都是通过Zygote进程fork出来的,我们在android.os.Process这个类里面可以看到android进程的启动过程有这么一句:
if&((debugFlags&&&Zygote.DEBUG_ENABLE_DEBUGGER)&!=&0)&{&&&&&&argsForZygote.add(&--enable-debugger&);&&}&&
也就是说,一个进程是否可以调试是由进程启动时候的参数决定的;普通的App进程如果是debug
keystore默认是可以调试的,有或者你在AndroidManifest里面指定debuggable为true也是可以调试的。对系统进程,我们只有采取系统级别的手段:让整个系统可以调试&&debug版或者编译参数debuggable为1的系统。
解决这个办法很简单:使用模拟器(真机也行,限Nexus系列刷原生Android系统,把系统启动的debuggable参数修改为1),我的Nexus 5
可以调试的进程如下:
可调试任意进程的设备
这样,系统中所有的Android进程都可以调试了;这一点很重要,比如你要分析Activity的启动流程,相当多一部分代码是在ActivityManagerService所在的进程system_server执行的,如果你把断点打在别的进程,就会产生跟丢了的情况。在比如,你要调试ActivityThread的main函数,在main函数里面执行了一句attach,最终调用AMS的attachApplication的时候,代码就通过Binder
IPC调用到了AMS的system_server进程。
明白你要执行的代码运行在哪一个进程相当重要,在Android中,由于Binder通信机制的存在,「进程迁移」使用的非常非常频繁,因此需要对binder机制有一定的了解;详细的话可以参考我的博客:Binder学习指南
如何在对应的代码处下断点?
假设我们现在把debugger
attach到了正确的进程,那么断点应该下在哪里呢?直观来讲,就是说我需要导入所有的Android源码吗?如果不是应该导入哪些代码,怎么导入?
首先,如果你需要调试的类在sdk里面导出了,你压根儿就不需要再导入源码,Android Studio自动帮你关联了这部分代码(前提是你用SDK
Manager下载了sdk的源码,如下图:
SDK manager下载源码
比如你要调试ActivityManagerServce类的attachApplication方法,那么很简单;创建一个空的Android项目,SDK版本选择与你要调试的模拟器/真机
的android相同(这很重要,下文会讲述);然后attrach
到system_server进程,直接在attach_application上面打上断点;随便启动一个app,可以看到我们熟悉的调试界面:
调试attachApplication
如果这部分类在sdk中没有导入(比如@hide)的,又或者压根儿不是SDK的类,(比如系统app的源码)那应该怎么办呢?直接导入这部分代码即可。不需要是Android项目,普通的Java项目即可;举个例子,假设你想调试原生Android系统的「系统设置」这个程序,该如何做呢?
根据上面的分析,我们首先得知道「系统设置&」运行在哪一个进程,通常情况下进程名字就是包名;我们查出设置的包名即可,而包名是在源码的AndroidManifeist中声明的,因此,我们找到「系统设置」这个程序的源码即可;源码在
https://android.googlesource.com/
,系统App的源码在/packages这个子目录下面,我们一个个找,最终可以确定「系统设置」的源码在https://android.googlesource.com/platform/packages/apps/Settings/然后我们把这部分代码git clone下来,导入Android Studio:
调试Settings
我们去AndroidManifest中查到,「系统设置」的包名为:com.android.settings,这样我们attach到这个进程 :
attach setting进程
然后,我们随便打个断点玩一玩,比如进入设置主界面的时候,断下来;我们在AndroidManifest中查到设置程序的入口界面为:Settings,我们在这个类的onCreate里面打一个断点,然后进入设置程序,发现完美滴断下来了:
在setting中断点成功
OK,到这里;应该学会如何在正确的位置打断点了:正确的进程,正确的位置。接下来,要完成调试,还需要一些技巧。
如何跟踪代码?
或许你会说,跟踪代码不就是step in/out/over么,这有什么难的?但其实事情并没有你想象的那么简单,要优雅滴调试,还是需要一些姿势的。
跟踪代码一个首要的问题是行号对应。如果你在正确位置下了断点,但是跟踪的时候,单步调试,发现运行的代码和Android
Studio里面的代码对不上号,那么就很蛋疼;要使得调试器的行号能够对应,必须保证设备上的代码和调试器的代码是同一份;简单来说,需要使用Android的原生系统(模拟器,Nexus系列真机),然后调试器里面使用的SDK版本,必须和设备的系统版本一致。
行号不对应怎么办?
一定要注意行号对应这一点,这会使调试过程简单很多;如果没有办法,行号对不上,那该如何调试呢?
行号不对应带来的一个首要问题就是,下断点的时候都有可能出现问题;比如你在TestClass的第100行下了一个断点,但是由于行号不对应,有可能真正执行的代码第100行是没有意义的空行或者是在下一个函数里面,这样断点就没有起到应有的作用了。
要解决行好对应的问题,必须使用方法断点;我们直接在某个函数的入口设置断点,这样即使行号对不上,也能在正确的入口出断下来,这一点非常重要。
解决了如何下断点的问题,那么行号不对应,怎么知道执行到哪了,怎么查看局部变量?
在Android Studio的调试器的左边,显示了每一个线程执行的栈桢,栈桢里面包含了当前线程丰富的信息:
看到没,真正运行的代码在哪一行,当前运行的是什么函数一目了然;接下来你在step
into/out的时候,不能以源代码的行数为准,而应该以这个栈桢所显示的代码行数为准。
熟练使用断点
OK,现在不论行号是否能对应,我们都能正确滴下断点调试了。断点有很多种类型,方法断点,watch
point,条件断点都能够很好滴辅助我们调试;如果你连这几个名词都没有听说过,一定要恶补一下;可以参阅我的博客:Android
Studio你不知道的调试技巧;我就不再复述了。
如果你仔细看完了本文和我给出的链接,那么应该对Debug技术不再陌生了;接下来你可以选择Framework层的代码,手动调试一下加深理解;在日后的工作过程中,不断滴加强debug技术的练习,让它称为你解决复杂问题的条件反射,一定会事半功倍!还有记住:
Debug the fucking source code.
【编辑推荐】
【责任编辑: TEL:(010)】
大家都在看猜你喜欢
热点头条热点头条头条
24H热文一周话题本月最赞
讲师:119680人学习过
讲师:28666人学习过
讲师:190050人学习过
CTO专属活动
精选博文论坛热帖下载排行
在网络应用越来越复杂的今天,传统的网络应用已经不能满足企业和用户的需要,这就对网络管理员、信息管理部门提出了更高的要求。本书介绍了...
订阅51CTO邮刊您所在位置: &
&nbsp&&nbsp&nbsp&&nbsp
栈回溯技术及uClibc的堆实现原理.doc 21页
本文档一共被下载:
次 ,您可全文免费在线阅读后下载本文档。
下载提示
1.本站不保证该用户上传的文档完整性,不预览、不比对内容而直接下载产生的反悔问题本站不予受理。
2.该文档所得收入(下载+内容+预览三)归上传者、原创者。
3.登录后可充值,立即自动返金币,充值渠道很便利
栈回溯技术及uClibc的堆实现原理
你可能关注的文档:
··········
··········
栈回溯技术及uClibc的堆实现原理
本文描述栈的作用、uClibc上堆的实现,利用栈回溯技术查找编程中经常发生的段错误问题,理解栈、堆的作用,通过几个例子分析越界访问导致的错误。
【关键词】
堆 栈 回溯 堆实现 栈作用
一、问题的提出
段错误、非法地址访问等问题导致程序崩溃的现象屡屡发生,如果能找到发生错误的函数,往往一眼就能看出BUG所在——对于这类比较简单的问题,比如使用空指针进行读写等,利用栈回溯技术可以很快定位。但是对于数组溢出、内存泄漏等问题导致的程序错误,往往隐藏很深,它们并不当场发作,即使我们一步一步跟踪到发生错误的语句时,也经常会让人觉得“这个地方根本不可能出错啊”——错误在很早以前就隐藏下来了,只不过是这个“不可能出错的语句”触发了它。了解栈的作用、堆的实现,可以让我们脑中对程序的运行、函数的调用、变量的操作有个感官的了解,对解决这类问题会有所帮助。
二、解决思路
了解了栈,就可以通过栈回溯技术分析程序的调用关系,从而得出程序出错的流程;了解了堆,就可以对各类动态分配、释放内存导致的错误有个指导思想。
(1)、栈的作用
一个程序包含代码段、数据段、BSS段、堆、栈;其中数据段用来中存储初始值不为0的全局数据,BSS段用来存储初始值为0的全局数据,堆用于动态内存分配,栈用于实现函数调用、存储局部变量。比如对于如下程序:
程序1 section.c
01 #include
02 #include
03 #include
05 int *g_pB
06 int g_iCount = 10;
08 int main(int argc, char **argv)
char str[2];
g_pBuf = malloc(g_iCount);
printf("Address of main
= 0x%08x\n", (unsigned int)main);
printf("Address of g_pBuf
= 0x%08x\n", (unsigned int)&g_pBuf);
printf("Address of g_iCount
= 0x%08x\n", (unsigned int)&g_iCount);
printf("Address of malloc buf
= 0x%08x\n", (unsigned int)g_pBuf);
printf("Address of local buf str
= 0x%08x\n", (unsigned int)str);
使用如下命令编译得到可执行文件section,反汇编文件section.dis:
mips-uclibc-gcc
mips-uclibc-objdump
section.dis
在T500上的linux环境下,这个程序的输出结果为:
Address of main
Address of g_pBuf
Address of g_iCount
Address of malloc buf
Address of local buf
= 0x7fff7e50
其中main函数的地址为0x,它处于代码段中;全局变量g_pBuf位于BSS段;全局变量g_iCount位于数据段;使用malloc分配出来的内存地址为0x,它位于堆中;局部变量str数组的开始地址为0x7fff7e50,位于栈中。它们的分布图示如下:
图1 程序各段示意图
栈的作用有二:
保存调用者的环境——某些寄存器的值、返回地址
存储局部变量
现在通过一个简单的例子来说明栈的作用:
程序2 stack.c
01 #include
02 #include
03 #include
05 void A(int a);
06 void B(int b);
07 void C(int c);
09 void A(int a)
printf("%d: A call B\n", a);
正在加载中,请稍后...Objective-C 之 栈(stack)与堆(heap) - 简书
Objective-C 之 栈(stack)与堆(heap)
相信iOS开发者都会常常听到"栈"与"堆"两个概念字,但是这两个概念到底是什么呢?下面来探讨一下:
栈是内存中的一块区域,都有方法或者函数被执行时,栈就会为方法或者函数分配自己的一部分内存空间,这部分内存空间称为桢(frame)。当某个应用启动并运行main函数时,main函数的桢会被保留在栈的底部,如果main调用别的方法或者函数,那么这个方法或者函数的桢就会压入栈的顶部,即main函数桢的上方。
堆也是内存中的一块区域,和栈是相对独立分开的。栈的顺序是后进先出保存桢的,而堆则是无序,如“堆”的顾名思义:一大堆,不按顺序。堆事通过指针保存对象在堆中的地址,当某个类使用alloc的时候,系统就会从堆中分配出一块内存空间。由于堆的空间是有限的,所以不能无上限创建对象,在ARC的帮助下,系统会帮助我们管理堆的空间,释放引用计数为0的对象。
JVM内存模型Java虚拟机(Java Virtual Machine=JVM)的内存空间分为五个部分,分别是: 程序计数器 Java虚拟机栈 本地方法栈 堆 方法区。下面对这五个区域展开深入的介绍。 程序计数器1.1. 什么是程序计数器?程序计数器是一块较小的内存空间,可...
这篇文章是我整合了网友们的文章,具体来源(哪位大牛写的我也不清楚)不过在此膜拜一下,学过C/C++的看一下或许有帮助。如果只是想了解一下请看上一篇 栈与堆 (一) 六、底层介绍堆(heap)和栈(stack)是C/C++编程不可避免会碰到的两个基本概念。首先,这两个概念都可...
从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗offer。我找的是java后台开发,把常见的问题分享给大家,有一些是自己的总结,有一些是网上借鉴的内容。希望能帮助到各位。预祝各位同学拿到自己心仪的offer...
“text segment ”是应用程序运行时应用程序代码存在的内存段。每一个指令,每一个单个函数、过程、方法和执行代码都存在这个内存段中直到应用程序退出。一般情况下,你不会真的不得不知道这个段的任何事情。 当应用开始以后,函数main() 被调用,一些空间分配在”stac...
喜欢的话记得点赞 一、内存管理:移动设备的内存及其有限,每一个APP所能占用的内存是有限制的二、什么行为会增加APP的内存占用?1、创建一个oc对象
2、定义一个变量
3、调用一个函数或者方法三、内存管理范围:1、任何继承了NSObject的对象2、对其它非对象类型无效...
You are what you eat,同理,我们的认知也取决于我们看的书以及我们理解的度。可见读书是件很重要的事情。但每个人读的书不一样,每个人理解的程度也不一样。个人觉得,理解程度的深浅很大程度取决于读书方式。 有人说,读书要快,读得多了,量变就变成质变,最后效果也就...
1.安装 Rabbit MQ 是建立在强大的Erlang OTP平台上,因此安装RabbitMQ之前要先安装Erlang。erlang下载rabbitmq下载 注意:1.默认安装的Rabbit MQ 监听端口是:5672 2.配置 erlang 环境变量配置 激活Rabbi...
我的心情很不好。 或者说,是在寻找一些东西。 之前看了九型人格,知道了自己是属于第三型实干型的人格。 优点是做事效率高,执行力,行动力强。 缺点是容易忘记自己的真实感受。 我希望得到别人的认可,所以很善于雕刻自己的面具,像一条变色龙。 每当想起自己想要什么,内心总是有着很多...
杭州自秦朝设县治以来已有2200多年的历史,曾是吴越国和南宋的都城,是中国八大古都之一。因风景秀丽,素有“人间天堂”的美誉。
杭州人文古迹众多,西湖及其周边有大量的自然及人文景观遗迹。
杭州,曾经因为一句“上有天堂,下有苏杭”而成为人们印象中天堂的化身,也曾...
生命不息,学习不止 生命不息,学习不止
T6学员:海霞
本着学习的初心来到九段实践课程学习,认识了不同领域却都有这同样想成为培训师的朋友。 听过几次讲座,欣赏过也赞赏过,却不了解这份职业的情况。
第一天的培训郑老师的从行业...}

我要回帖

更多关于 堆栈平衡原理 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信