面试创维时被问到了堆、栈的相关知识,故作此总结。本文为一博客的补充。
博客总结
堆、栈区别:
- 申请方式:栈是系统自动分配的,堆需要用malloc、new申请。
- 管理方式:系统分配栈时,如果有剩余空间就分配;分配堆时,从空间地址(链表)中找到一个大于所需空间的内存块,分配给程序,如果这个空闲块分配后还有剩余空间,则将剩余部分重新放入链表中。
- 效率:栈的分配效率比堆的效率高。
- 大小:一般栈比堆小。
- 存储内容:看本文即可。
- 存取效率:栈比堆快(待验证)。
C++内存布局概述
包括4个部分:
- 代码段:只读,存放代码。
- 数据段:又分为只读数据段和可读写数据段。只读数据段存储常量、字符串字面值(global)等,可读写数据段存储静态变量(包括全局、局部静态变量)。
- 堆:动态分配的内存。
- 栈:局部变量。
Code
接下来分析一下具体的代码。
使用gdb查看内存分布。
// C语言源程序
#include <cstdlib>
int ga;
const int gb = 1;
static int gc;
static const int gd = 1;
int main() {
int la;
const int lb = 1;
static int lc;
static const int ld = 1;
int *p = (int*)malloc(sizeof(int));
lc = la; // 使用la,避免编译器优化掉未使用的变量
return 0;
}
// gdb查看内存分布情况(在编译时使用 gcc -O0 -g a.cc,-O0关闭了编译优化选项)
// 0x555555555000 开头的为代码段
// 0x555555556000 为只读数据段
// 0x555555558000 为可读写数据段
// 0x555555559000 为堆
// 0x7ffffffde000 为栈
(gdb) info proc mappings
process 11481
Mapped address spaces:
Start Addr End Addr Size Offset Perms objfile
0x555555554000 0x555555555000 0x1000 0x0 r--p /home/hxd/algo/a.out
0x555555555000 0x555555556000 0x1000 0x1000 r-xp /home/hxd/algo/a.out
0x555555556000 0x555555557000 0x1000 0x2000 r--p /home/hxd/algo/a.out
0x555555557000 0x555555558000 0x1000 0x2000 r--p /home/hxd/algo/a.out
0x555555558000 0x555555559000 0x1000 0x3000 rw-p /home/hxd/algo/a.out
0x555555559000 0x55555557a000 0x21000 0x0 rw-p [heap]
0x7ffff7d8b000 0x7ffff7d8e000 0x3000 0x0 rw-p
0x7ffff7d8e000 0x7ffff7db6000 0x28000 0x0 r--p /usr/lib/x86_64-linux-gnu/libc.so.6
0x7ffff7db6000 0x7ffff7f4b000 0x195000 0x28000 r-xp /usr/lib/x86_64-linux-gnu/libc.so.6
0x7ffff7f4b000 0x7ffff7fa3000 0x58000 0x1bd000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6
0x7ffff7fa3000 0x7ffff7fa7000 0x4000 0x214000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6
0x7ffff7fa7000 0x7ffff7fa9000 0x2000 0x218000 rw-p /usr/lib/x86_64-linux-gnu/libc.so.6
0x7ffff7fa9000 0x7ffff7fb6000 0xd000 0x0 rw-p
0x7ffff7fbb000 0x7ffff7fbd000 0x2000 0x0 rw-p
0x7ffff7fbd000 0x7ffff7fc1000 0x4000 0x0 r--p [vvar]
0x7ffff7fc1000 0x7ffff7fc3000 0x2000 0x0 r-xp [vdso]
0x7ffff7fc3000 0x7ffff7fc5000 0x2000 0x0 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x7ffff7fc5000 0x7ffff7fef000 0x2a000 0x2000 r-xp /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x7ffff7fef000 0x7ffff7ffa000 0xb000 0x2c000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x7ffff7ffb000 0x7ffff7ffd000 0x2000 0x37000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x7ffff7ffd000 0x7ffff7fff000 0x2000 0x39000 rw-p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 rw-p [stack]
// 查看各种类型的变量分别存储在哪个内存区域
(gdb) print &ga
$1 = (int *) 0x555555558014 <ga>
(gdb) print &gb
$2 = (const int *) 0x555555556004 <gb>
(gdb) print &gc
$3 = (int *) 0x555555558018 <gc>
(gdb) print &gd
$4 = (const int *) 0x555555556008 <gd>
(gdb) print &la
$5 = (int *) 0x7fffffffded4
(gdb) print &lb
$6 = (const int *) 0x7fffffffded0
(gdb) print &lc
$7 = (int *) 0x55555555801c <main::lc>
(gdb) print &ld
$8 = (const int *) 0x55555555600c <main::ld>
(gdb) print p
$9 = (int *) 0x5555555592a0
结论:
- 全局变量存放在只读数据段(const)或者可读写数据段(非const);
- 局部变量存放在栈(非static)或者数据段(static const在只读数据段,static非const在可读写数据段)。
对于字符串字面值,情况有一点差别:
const char *s1 = "hello"; // s1本身在栈,但它指向的字符串字面值在只读数据段
const char s2[] = "hello"; // s2存放在栈中