1、預備知識—程序的內存分配
一個由c/C++編譯的程序佔用的內存分爲如下幾個部分
一、棧區(stack)— 由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。其操做方式相似於數據結構中的棧。
二、堆區(heap) — 通常由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式卻是相似於鏈表。
三、全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域(.data),未初始化的全局變量和未初始化的靜態變量在相鄰的另外一塊區域(.bss)。 - 程序結束後由系統釋放。
四、文字常量區 —常量字符串就是放在這裏的(.rodata)。 程序結束後由系統釋放。
五、程序代碼區—存放函數體的二進制代碼(.text)。
2、例子程序
這是一個前輩寫的,很是詳細
//main.cpp
int a = 0; // 全局初始化區
char *p1; // 全局未初始化區
main()
{
int b; // 棧區
char s[] = "abc"; // 棧區
char *p2; // 棧區
char *p3 = "123456"; // "123456/0" 在常量區,p3在棧區
static int c =0; // 全局(靜態)初始化區
p1 = (char *)malloc(10);
p2 = (char *)malloc(20); // 分配得來的10和20字節的區域就在堆區
strcpy(p1, "123456"); // "123456/0" 放在常量區,編譯器可能會將它
// 與p3所指向的"123456"優化成一個地方。
}
|
==============================================================
static全局變量與普通的全局變量有什麼區別?static局部變量和普通局部變量有什麼區別?static函數與普通函數有什麼區別?
static全局變量與普通的全局變量有什麼區別?static局部變量和普通局部變量有什麼區別?static函數與普通函數有什麼區別?
答:
1) 全局變量(外部變量)的說明以前再冠以static 就構成了靜態的全局變量。全局變量自己就是靜態存儲方式, 靜態全局變量固然也是靜態存儲方式。 這二者在存儲方式上並沒有不一樣。這二者的區別在於非靜態全局變量的做用域是整個源程序, 當一個源程序由多個源文件組成時,非靜態的全局變量在各個源文件中都是有效的。 而靜態全局變量則限制了其做用域, 即只在定義該變量的源文件內有效, 在同一源程序的其它源文件中不能使用它。因爲靜態全局變量的做用域侷限於一個源文件內,只能爲該源文件內的函數公用,所以能夠避免在其它源文件中引發錯誤。
2) 從以上分析能夠看出, 把局部變量改變爲靜態變量後是改變了它的存儲方式即改變了它的生存期。把全局變量改變爲靜態變量後是改變了它的做用域,限制了它的使用範圍。
3) static函數與普通函數做用域不一樣,僅在本文件。只在當前源文件中使用的函數應該說明爲內部函數(static),內部函數應該在當前源文件中說明和定義。對於可在當前源文件之外使用的函數,應該在一個頭文件中說明,要使用這些函數的源文件要包含這個頭文件
綜上所述:
static全局變量與普通的全局變量有什麼區別:
static全局變量只初使化一次,防止在其餘文件單元中被引用;
static局部變量和普通局部變量有什麼區別:
static局部變量只被初始化一次,下一次依據上一次結果值;
static函數與普通函數有什麼區別:
static函數在內存中只有一份,普通函數在每一個被調用中維持一份拷貝
==============================================================
一個C語言變量分配的實際例子:
咱們來看看在可執行文件中,變量們會被分配在哪些區裏.這裏以可執行文件爲例子,可執行文件有固定的內存加載地址,符號(函數/變量的名字)未來在內存裏的地址鏈接器是能夠提早肯定的。
源程序編譯鏈接的結果是造成1堆彙編指令代碼,大體分爲.text .data .bss等幾個節區(section)。對於.exe文件和.so文件,全局和靜態變量都放在.data 或.bss段(gas把源文件從頭至尾掃描1遍,才知道一個變量的所有狀況:是否認義;類型;是否初始化。而後把初始化的變量在.data段裏分配位置和空間,把沒初始化的變量在.bss段裏分配位置和空間,沒定義的變量分配在.undef段)。彙編指令代碼裏全局變量表現爲一個內存地址(全局變量在目標文件裏是一個偏移值,加載進內存裏是一個內存地址)。臨時變量在彙編代碼裏變成ebp/esp+n,表現爲一個堆棧地址,化爲程序正文(.text)的一部分。有些變量的最終內存地址在加載進內存以前還不能肯定,須要加載進內存才能夠計算出來.
全局變量 做用域是跨越多個源程序的。所以全局變量不能重名。靜態變量做用域是位於單個源程序內。多個源程序能夠有同名的全局靜態變量。本例中,爲了區分多個同名的靜態變量,gcc 用 c444和c444.0 來加以區別。
[test@redhat]# more aaa.c
# include <stdio.h>
int a111 = 0; # 全局變量 已初始化
char *p111 = "654321"; # 全局指針變量 已經初始化
static int c444 = 9; # 靜態全局變量 已經初始化
static int c555; # 靜態全局變量 未初始化
main()
{
int b222; # 局部變量
char s333[] = "abc"; # 局部變量
char *p222; # 局部變量
char *p333 = "123456"; # 局部變量
static int c444 =0; # 已初始化靜態局部變量,與前面靜態全局變量重名
p111 = (char *)malloc(10);
p222 = (char *)malloc(20);
strcpy(p111, "123456");
}
[test@redhat]# gcc -o aaa ./aaa.c
[test@redhat]# readelf -a ./aaa
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x80482d0
Start of program headers: 52 (bytes into file)
Start of section headers: 2040 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 7
Size of section headers: 40 (bytes)
Number of section headers: 27
Section header string table index: 24
Section Headers: Addr是文件加載進內存時,每一個section在內存中的虛擬地址
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048114 000114 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048128 000128 000020 00 A 0 0 4
[ 3] .hash HASH 08048148 000148 00002c 04 A 4 0 4
[ 4] .dynsym DYNSYM 08048174 000174 000060 10 A 5 1 4
[ 5] .dynstr STRTAB 080481d4 0001d4 000053 00 A 0 0 1
[ 6] .gnu.version VERSYM 08048228 000228 00000c 02 A 4 0 2
[ 7] .gnu.version_r VERNEED 08048234 000234 000020 00 A 5 1 4
[ 8] .rel.dyn REL 08048254 000254 000008 08 A 4 0 4
[ 9] .rel.plt REL 0804825c 00025c 000018 08 A 4 b 4
[10] .init PROGBITS 08048274 000274 000017 00 AX 0 0 4
[11] .plt PROGBITS 0804828c 00028c 000040 04 AX 0 0 4
[12] .text PROGBITS 080482d0 0002d0 0001e4 00 AX 0 0 16
[13] .fini PROGBITS 080484b4 0004b4 00001b 00 AX 0 0 4
[14] .rodata PROGBITS 080484d0 0004d0 00001a 00 A 0 0 4
[15] .eh_frame PROGBITS 080484ec 0004ec 000004 00 A 0 0 4
[16] .data PROGBITS 080494f0 0004f0 00001c 00 WA 0 0 4
[17] .dynamic DYNAMIC 0804950c 00050c 0000c8 08 WA 5 0 4
[18] .ctors PROGBITS 080495d4 0005d4 000008 00 WA 0 0 4
[19] .dtors PROGBITS 080495dc 0005dc 000008 00 WA 0 0 4
[20] .jcr PROGBITS 080495e4 0005e4 000004 00 WA 0 0 4
[21] .got PROGBITS 080495e8 0005e8 00001c 04 WA 0 0 4
[22] .bss NOBITS 08049604 000604 000008 00 WA 0 0 4
[23] .comment PROGBITS 00000000 000604 000126 00 0 0 1
[24] .shstrtab STRTAB 00000000 00072a 0000ce 00 0 0 1
[25] .symtab SYMTAB 00000000 000c30 0004c0 10 26 2f 4
[26] .strtab STRTAB 00000000 0010f0 000275 00 0 0 1
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
INTERP 0x000114 0x08048114 0x08048114 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x004f0 0x004f0 R E 0x1000
LOAD 0x0004f0 0x080494f0 0x080494f0 0x00114 0x0011c RW 0x1000
DYNAMIC 0x00050c 0x0804950c 0x0804950c 0x000c8 0x000c8 RW 0x4
NOTE 0x000128 0x08048128 0x08048128 0x00020 0x00020 R 0x4
STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
Symbol table '.symtab' contains 76 entries:
對於.exe文件,符號的Value 是未來加載進內存中的真實地址;對於.so文件Value須要重定位.
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 08048114 0 SECTION LOCAL DEFAULT 1
2: 08048128 0 SECTION LOCAL DEFAULT 2
3: 08048148 0 SECTION LOCAL DEFAULT 3
4: 08048174 0 SECTION LOCAL DEFAULT 4
5: 080481d4 0 SECTION LOCAL DEFAULT 5
6: 08048228 0 SECTION LOCAL DEFAULT 6
7: 08048234 0 SECTION LOCAL DEFAULT 7
8: 08048254 0 SECTION LOCAL DEFAULT 8
9: 0804825c 0 SECTION LOCAL DEFAULT 9
10: 08048274 0 SECTION LOCAL DEFAULT 10
11: 0804828c 0 SECTION LOCAL DEFAULT 11
12: 080482d0 0 SECTION LOCAL DEFAULT 12
13: 080484b4 0 SECTION LOCAL DEFAULT 13
14: 080484d0 0 SECTION LOCAL DEFAULT 14
15: 080484ec 0 SECTION LOCAL DEFAULT 15
16: 080494f0 0 SECTION LOCAL DEFAULT 16
17: 0804950c 0 SECTION LOCAL DEFAULT 17
18: 080495d4 0 SECTION LOCAL DEFAULT 18
19: 080495dc 0 SECTION LOCAL DEFAULT 19
20: 080495e4 0 SECTION LOCAL DEFAULT 20
21: 080495e8 0 SECTION LOCAL DEFAULT 21
22: 08049604 0 SECTION LOCAL DEFAULT 22
23: 00000000 0 SECTION LOCAL DEFAULT 23
24: 00000000 0 SECTION LOCAL DEFAULT 24
25: 00000000 0 SECTION LOCAL DEFAULT 25
26: 00000000 0 SECTION LOCAL DEFAULT 26
27: 080482f4 0 FUNC LOCAL DEFAULT 12 call_gmon_start
28: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
29: 080495d4 0 OBJECT LOCAL DEFAULT 18 __CTOR_LIST__
30: 080495dc 0 OBJECT LOCAL DEFAULT 19 __DTOR_LIST__
31: 080484ec 0 OBJECT LOCAL DEFAULT 15 __EH_FRAME_BEGIN__
32: 080495e4 0 OBJECT LOCAL DEFAULT 20 __JCR_LIST__
33: 080494f8 0 OBJECT LOCAL DEFAULT 16 p.0
34: 08049604 1 OBJECT LOCAL DEFAULT 22 completed.1
35: 08048320 0 FUNC LOCAL DEFAULT 12 __do_global_dtors_aux
36: 08048360 0 FUNC LOCAL DEFAULT 12 frame_dummy
37: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
38: 080495d8 0 OBJECT LOCAL DEFAULT 18 __CTOR_END__
39: 080495e0 0 OBJECT LOCAL DEFAULT 19 __DTOR_END__
40: 080484ec 0 OBJECT LOCAL DEFAULT 15 __FRAME_END__
41: 080495e4 0 OBJECT LOCAL DEFAULT 20 __JCR_END__
42: 08048490 0 FUNC LOCAL DEFAULT 12 __do_global_ctors_aux
43: 00000000 0 FILE LOCAL DEFAULT ABS aaa.c
44: 08049504 4 OBJECT LOCAL DEFAULT 16 c444 # static變量爲LOCAL綁定屬性(也即做用域) 已初始化靜態變量存放在.data
45: 08049508 4 OBJECT LOCAL DEFAULT 16 c444.0 # 已初始化靜態變量存放在.data (多個源文件能夠定義同名的靜態變量)
46: 08049608 4 OBJECT LOCAL DEFAULT 22 c555 # 未初始化靜態變量存放在.bss
47: 080494fc 4 OBJECT GLOBAL DEFAULT 16 a111 # 全局變量爲GLOBAL綁定屬性 已初始全局變量存放在.data
48: 0804950c 0 OBJECT GLOBAL DEFAULT 17 _DYNAMIC
49: 080484d0 4 OBJECT GLOBAL DEFAULT 14 _fp_hw
50: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __fini_array_end
51: 080494f4 0 OBJECT GLOBAL HIDDEN 16 __dso_handle
52: 08048440 66 FUNC GLOBAL DEFAULT 12 __libc_csu_fini
53: 08048274 0 FUNC GLOBAL DEFAULT 10 _init
54: 0804829c 427 FUNC GLOBAL DEFAULT UND
malloc@@GLIBC_2.0 55: 080482d0 0 FUNC GLOBAL DEFAULT 12 _start
56: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __fini_array_start
57: 080483f0 71 FUNC GLOBAL DEFAULT 12 __libc_csu_init
58: 08049500 4 OBJECT GLOBAL DEFAULT 16 p111
59: 08049604 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
60: 0804838c 89 FUNC GLOBAL DEFAULT 12 main
61: 080482ac 251 FUNC GLOBAL DEFAULT UND
__libc_start_main@@GLIBC _
62: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __init_array_end
63: 080494f0 0 NOTYPE WEAK DEFAULT 16 data_start
64: 080484b4 0 FUNC GLOBAL DEFAULT 13 _fini
65: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __preinit_array_end
66: 08049604 0 NOTYPE GLOBAL DEFAULT ABS _edata
67: 080495e8 0 OBJECT GLOBAL DEFAULT 21 _GLOBAL_OFFSET_TABLE_
68: 0804960c 0 NOTYPE GLOBAL DEFAULT ABS _end
69: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __init_array_start
70: 080484d4 4 OBJECT GLOBAL DEFAULT 14 _IO_stdin_used
71: 080494f0 0 NOTYPE GLOBAL DEFAULT 16 __data_start
72: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
73: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __preinit_array_start
74: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
75: 080482bc 48 FUNC GLOBAL DEFAULT UND
strcpy@@GLIBC_2.0
文件./aaa加載進內存後,再看看變量的地址以及所在的區:
[test@redhat]# gdb ./aaa
GNU gdb 6.1.1
(gdb) disassemble main
Dump of assembler code for function main:
0x0804838c <main+0>: push %ebp
0x0804838d <main+1>: mov %esp,%ebp
0x0804838f <main+3>: sub $0x18,%esp
0x08048392 <main+6>: and $0xfffffff0,%esp
0x08048395 <main+9>: mov $0x0,%eax
0x0804839a <main+14>: sub %eax,%esp
# char s333[] = "abc";
0x0804839c <main+16>: mov 0x80484df,%eax # 0x80484df處爲"abc",位於.rodata
0x080483a1 <main+21>: mov %eax,0xfffffff8(%ebp) # 0xfffffff8(%ebp) 爲局部變量 char s333[]
# char *p333 = "123456";
0x080483a4 <main+24>: movl $0x80484e3,0xfffffff0(%ebp) # 0x80484e3處爲"123456/0",位於.rodata; 0xfffffff0(%ebp) 爲局部變量 char *p333
# p111 = (char *)malloc(10);
0x080483ab <main+31>: sub $0xc,%esp
0x080483ae <main+34>: push $0xa # 0xa=10 ; push $0xa後,此時堆棧esp值又減去4字節,至關於sub $0x10,%esp
0x080483b0 <main+36>: call 0x804829c <malloc>
0x080483b5 <main+41>: add $0x10,%esp
0x080483b8 <main+44>: mov %eax,0x8049500 # 0x8049500 爲全局變量p111,位於.data
# p222 = (char *)malloc(20);
0x080483bd <main+49>: sub $0xc,%esp
0x080483c0 <main+52>: push $0x14 # 0x14=20 ; push $0xa後,此時堆棧esp值又減去4字節,至關於sub $0x10,%esp
0x080483c2 <main+54>: call 0x804829c <malloc>
0x080483c7 <main+59>: add $0x10,%esp
0x080483ca <main+62>: mov %eax,0xfffffff4(%ebp) # 0xfffffff4(%ebp) 爲局部變量p222
# strcpy(p111, "123456");
0x080483cd <main+65>: sub $0x8,%esp
0x080483d0 <main+68>: push $0x80484e3 # 0x80484e3處內容爲"123456/0",位於.rodata;
0x080483d5 <main+73>: pushl 0x8049500 # 0x8049500 爲全局變量p111,位於.data
0x080483db <main+79>: call 0x80482bc <strcpy>
0x080483e0 <main+84>: add $0x10,%esp
0x080483e3 <main+87>: leave
0x080483e4 <main+88>: ret
0x080483e5 <main+89>: nop
0x080483e6 <main+90>: nop
0x080483e7 <main+91>: nop
0x080483e8 <main+92>: nop
0x080483e9 <main+93>: nop
0x080483ea <main+94>: nop
0x080483eb <main+95>: nop
0x080483ec <main+96>: nop
0x080483ed <main+97>: nop
0x080483ee <main+98>: nop
0x080483ef <main+99>: nop
End of assembler dump.
(gdb) q
[test@redhat]#