栈的英文是stack,堆的英文是heap,很多人把stact翻译成堆栈,是不对的。
栈由栈帧组成
。当一个函数调用时,栈会为这个函数分配一个栈帧,用于存储函数的实参、局部变量、返回值,以及函数内调用函数时,当前函数的一些寄存器的相关信息(比如用于指向下一条指令的程序计数器)。
可以认为,一个栈帧存储一个函数。
当该函数调用结束后,栈帧会自动从栈上移去(这解释了局部变量的生存期问题)。
由于函数的嵌套,栈中可能有多个栈帧,此时,
最后分配的栈帧称为栈顶
,如下图所示。栈指针寄存器用于存储栈顶的地址,以跟踪栈顶。
下面,我们使用GDB查看一个实例的栈信息。实例程序gdbtest.cpp如下:
#include <stdio.h>
int square(int a, int b){
int ret = a*a + b*b;
return ret;
int doCalc(int num1, int num2){
int ret = square(num1, num2);
return ret;
int main(){
int param1 = 1;
int param2 = 2;
int result = doCalc(param1,param2);
printf("result is%d\n",result);
编译:g++ -g gdbtest.cpp
启动gdb:gdb a.out。
由源文件可知,main函数里面嵌套doCalc函数,doCalc函数又嵌套square函数,因此,这三个函数在栈中的分配如上图所示。在square函数中设置断点,程序停在断点后,使用backtrace命令,即可看到栈内容,如下所示:
(gdb) backtrace
#0 square (a=1, b=2) atgdbtest.cpp:6
#1 0x00000000004005e6 in doCalc(num1=1, num2=2) at gdbtest.cpp:10
#2 0x0000000000400613 in main ()at gdbtest.cpp:17
每一行前面的序号,就是栈帧在栈中的序号,#0表示栈顶,#1表示下一个栈帧,以此类推。使用frame 1,可以选择1号栈帧,使用info locals可以当前栈帧的局部变量:
(gdb) backtrace
#0 square (a=1, b=2) at gdbtest.cpp:6
#1 0x00000000004005e6 in doCalc (num1=1, num2=2) at gdbtest.cpp:10
#2 0x0000000000400613 in main () at gdbtest.cpp:17
(gdb) frame 1
#1 0x00000000004005e6 in doCalc (num1=1, num2=2) at gdbtest.cpp:10
10 int ret = square(num1, num2);
(gdb) info locals
ret = 0
(gdb)
GDB
中为我们提供了查看
内存
的命令 x,它是 examine 命令的简写,使用 x 命令查看
内存
数据的格式有很多种,这里的
内存
数据可以是任意类型。命令的详细介绍如下。
参数 f 的可选值:
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
参数 u 的可选值:
b 表示单字节
h 表示双字节
w 表示四字节
g 表示八
二、
内存
泄漏检查和调试
2.1
内存
泄漏
内存
泄漏是指程序中已动态分配的堆
内存
由于某种原因程序未释放或无法释放,由于一个进程的堆空间始终是有限的,32位的程序最多可以使用的
内存
不会超过2GB,如果代码中存在
内存
泄漏,短时间内程序不会崩溃,但是长时间运行直到没有
内存
可分配时,程序会崩溃。
调用堆
栈
是当前函数之前的所有已调用函数的列表,每个函数及其变量都被分配了一个”
栈
帧”,使用
GDB
查看函数调用堆
栈
可清晰地看到各个函数的调用顺序以及各函数的输入形参值,是分析程序的执行流程和输入依赖的重要手段。
为了便于讲解,本文基于下述通过递归算法计算斐波拉契数列的简单 demo 进行举例说明。`#include<stdio.h>
#include<stdlib.h>
int fibonacci(int n)
if (n == 1 || n == 2)
return..
内存
断点可以帮助我们查找一些
内存
的问题, 而
内存
问题往往都是比较棘手的问题。所以掌握
GDB
的
内存
调试还是很有用的,下来我们就开始
GDB
的
内存
断点之旅。 我们通过一个程序来学些这个方法: 注意涉及的指令就是rwatch、watch和awatch,分别表示读、写、读写。需要注意的是我用的这个版本直接用地址是断不下来的,必须用*(char*)之类来进行强制类型转换,不知道其它
当程序进行函数调用时,这些调用信息(比如在哪里调用等)称为
栈
帧。每一个
栈
帧的内容还包括调用函数的参数、局部变量等。所有
栈
帧组成的信息称为调用
栈
(或者调用堆
栈
)。当程序刚开始运行时,只有一个
栈
帧,即主函数 。每调用一个函数,就产生一个新的
栈
帧;当函数调用结束时(即从函数返回后),该函数的调用随之结束,该
栈
帧也结束。如果该函数是一个递归函数,则调用该函数会产生多个
栈
帧。查看
栈
回溯信息的命令是 。执行该
栈
回溯命令后,会显示程序执行到什么位置、包含哪些帧等信息。每一帧都有一个编号,从 0 开始。0 表示当前正在执