Linux将运行着的C程序分成不同的段,有些段的大小是固定的,有些是可变动的。
1. STACK
在HEAP之上,唯一一个由高地址向低地址增长的段。存放函数中的局部变量、临时变量、函数参数、返回地址、寄存器值等(文字常量和静态变量除外)。所有申请和销毁的操作由编译器负责。
2. HEAP
在BSS之上,向高地址增长,通过malloc/free等申请/销毁。内部实现时所有空闲空间由链表串联起来,频繁的申请和销毁容易形成碎片。所有申请和销毁的操作由应用程序负责。
3. BSS(Blocked Started by Symbol)
存放未初始化的全局变量和static变量,整个区段在程序启动时被初始化为0。
4. DATA
存放已初始化的全局变量和static变量,文字常量,全局常量。数据段所包含的数据的生命周期持续到程序结束。Linux将数据段分为已初始化的只读数据段(Read-only)和已初始化的读写数据段(Read-write)。
5. CODE/TEXT
代码段存放可执行代码,一般来说大小固定,并且只读。通常处于最底部,即最低地址部分。
-------------------
| STACK | <-高地址
-------------------
| HEAP |
-------------------
| BSS |
-------------------
| DATA |
-------------------
| CODE/TEXT | <-低地址
-------------------
#include <stdio.h>
//
// Name Size VMA LMA File off Algn
// .text 0000057c 08048390 08048390 00000390 2**4
// CONTENTS, ALLOC, LOAD, READONLY, CODE
// .rodata 000002d2 08048928 08048928 00000928 2**2
// CONTENTS, ALLOC, LOAD, READONLY, DATA
// .data 00000020 0804a014 0804a014 00001014 2**2
// CONTENTS, ALLOC, LOAD, DATA
// .bss 00000024 0804a034 0804a034 00001034 2**2
// ALLOC
//
int g_int_1;
// ADDRESS(g_int_1) = 0x804a044(bss)
int g_int_2 = 10;
// ADDRESS(g_int_2) = 0x804a01c(data)
static int g_static_1;
// ADDRESS(g_static_1) = 0x804a03c(bss)
static int g_static_2 = 10;
// ADDRESS(g_static_2) = 0x804a020(data)
const int g_const_1 = 10;
// ADDRESS(g_const_1) = 0x8048930(read-only data)
/* const int g_const_2; Compile Error: const object must be initialized if not extern*/
char g_ch_array_1[10];
// VALUE(g_ch_array_1) = 0x804a048(bss)
char g_ch_array_2[] = "abc";
// VALUE(g_ch_array_2) = 0x804a024("abc" in data).
// ADDRESS(g_ch_array_2) = 0x804a024(g_ch_array_2 in data).
char *g_p_string_1;
// VALUE(g_p_string_1) = NULL(g_p_string_1 in bss)
char *g_p_string_2 = "abc";
// VALUE(g_p_string_2) = 0x8048934("abc" in read-only data).
// ADDRESS(g_p_string_2) = 0x804a028(g_p_string_2 in data segment).
const char g_c_ch_array_1[] = "abc";
// VALUE(g_c_ch_array_1) = 0x8048938("abc" in read-only data).
// ADDRESS(g_c_ch_array_1) = 0x8048938(g_c_ch_array_1 in read-only data).
char const g_c_ch_array_2[] = "abc";
// VALUE(g_c_ch_array_2) = 0x804893c("abc" in read-only data).
// ADDRESS(g_c_ch_array_2) = 0x804893c(g_c_ch_array_2 in read-only data).
const char *g_c_p_string_1 = "abc";
// VALUE(g_c_p_string_1) = 0x8048934("abc" in read-only data).
// ADDRESS(g_c_p_string_1) = 0x804a02c(g_c_p_string_1 in data segment).
char * const g_c_p_string_2 = "abc";
// VALUE(g_c_p_string_2) = 0x8048934("abc" in read-only data).
// ADDRESS(g_c_p_string_2) = 0x8048940(g_c_p_string_2 in read-only data).
void main()
{
int l_int_1;
// ADDRESS(l_int_1) = 0xbf8be29c(stack)
int l_int_2 = 10;
// ADDRESS(l_int_2) = 0xbf8be298(stack)
static int l_static_1;
// ADDRESS(l_static_1) = 0x804a040(bss)
static int l_static_2 = 10;
// ADDRESS(l_static_2) = 0x804a030(data)
const int l_const_1 = 10;
// ADDRESS(l_const_1) = 0xbf8be294(stack)
char l_ch_array_1[10];
// VALUE(l_ch_array_1) = 0xbf8be2a6(stack)
char l_ch_array_2[] = "abc";
// VALUE(l_ch_array_2) = 0xbf8be2b8("abc" in stack).
// ADDRESS(l_ch_array_2) = 0xbf8be2b8(l_ch_array_2 in stack)
char *l_p_string_1;
// VALUE(l_p_string_1) = random(l_p_string_1 in stack)
char *l_p_string_2 = "abc";
// VALUE(l_p_string_2) = 0x8048934("abc" in read-only data).
// ADDRESS(l_p_string_2) = 0xbf8be290(l_p_string_2 in stack)
const char l_c_ch_array_1[] = "abc";
// VALUE(l_c_ch_array_1) = 0xbf8be2b4("abc" in stack).
// ADDRESS(l_c_ch_array_1) = 0xbf8be2b4(l_c_ch_array_1 in stack).
char const l_c_ch_array_2[] = "abc";
// VALUE(l_c_ch_array_2) = 0xbf8be2b0("abc" in stack).
// ADDRESS(l_c_ch_array_2) = 0xbf8be2b0(l_c_ch_array_2 in stack).
const char *l_c_p_string_1 = "abc";
// VALUE(l_c_p_string_1) = 0x8048934("abc" in read-only data).
// ADDRESS(l_c_p_string_1) = 0xbf8be28c(l_c_p_string_1 in stack).
char * const l_c_p_string_2 = "abc";
// VALUE(l_c_p_string_2) = 0x8048934("abc" in read-only data).
// ADDRESS(l_c_p_string_2) = 0xbf8be288(l_c_p_string_2 in stack).
}
- Note 1:区分字符数组(Char Array)和文字常量(String Literal)。考虑两种情况char s[] = "abc"和char *s = "abc",其中前者"abc"是字符数组,后者"abc"是文字常量。两者的区别是字符数组存放在stack/data segment,其内容可修改。而文字常量存放在read-only data segment,内容不可修改。
- Note 2:字符数组的地址。字符数组存放的位置与当前作用域相关,如果作用域在全局,则存放在data segment,如果作用域在局部,则存放在stack。定义char s[] = "abc",那么s == &s。
- Note 3:函数中的常量存放在stack。
- Note 4:函数中的文字常量存放在read-only data segment。
- Note 5:函数中的静态变量存放在bss/data segment。