C語言中數組在內存中是怎樣表示的,今天就給大家聊聊這個話題。
開局一張圖:
這個是經典的Linux進程內存布局,通常我們使用的數據存在這樣幾個地方:
- 棧區,Stack
- 全局區,Global
- 堆區,Heap
接下來我們分別看一下C語言中的數組在這幾個區域是怎樣表示的,注意,小風哥的機器是x86 64位。
數組與棧區
來看一段極其簡單的代碼:
void arr_on_stack() {
int arr[6];
arr[0]=100;
arr[1]=200;
arr[2]=300;
arr[3]=400;
arr[4]=500;
arr[5]=600;
int a = arr[0];
}
我們定義了一個局部變量arr作為int類型的數組,然后分別將100-600寫到了數組中,那么數組arr在內存中是怎樣表示的呢?
首先我們編譯一下:
# gcc -g -fno-stack-protector a.c
注意,-fno-stack-protector選項是為了禁止堆棧保護,讓匯編更容易懂些,關于堆棧保護這個話題可以參考這篇文章《黑客攻防:緩沖區溢出攻擊與堆棧保護》。
好啦,一切準備就緒,可以庖丁解牛啦,使用的刀就是gdb,代碼面前了無秘密,gdb面前程序的運行時(run time)了無秘密。
用gdb來調試剛剛編譯出來的程序,這里看一下arr_on_stack函數的匯編指令:
(gdb) disassemble arr_on_stack
Dump of assembler code for function arr_on_stack:
0x0000000000400526 <+0>: push %rbp
0x0000000000400527 <+1>: mov %rsp,%rbp
0x000000000040052a <+4>: movl $0x64,-0x20(%rbp)
0x0000000000400531 <+11>: movl $0xc8,-0x1c(%rbp)
0x0000000000400538 <+18>: movl $0x12c,-0x18(%rbp)
0x000000000040053f <+25>: movl $0x190,-0x14(%rbp)
0x0000000000400546 <+32>: movl $0x1f4,-0x10(%rbp)
0x000000000040054d <+39>: movl $0x258,-0xc(%rbp)
=> 0x0000000000400554 <+46>: mov -0x20(%rbp),%eax
0x0000000000400557 <+49>: mov %eax,-0x4(%rbp)
0x000000000040055a <+52>: nop
0x000000000040055b <+53>: pop %rbp
0x000000000040055c <+54>: retq
End of assembler dump.
我們在之前的文章《函數在內存中是怎樣表示的?》多次提到過,每個函數在運行起來后都有屬于自己的棧幀,棧幀組成棧區,此時arr_on_stack這個函數的棧區在哪里呢?答案就在寄存器rbp中。
我們來看一下rbp寄存器指向了哪里?
(gdb) p $rbp
$3 = (void *) 0x7ffffffee2a0
啊哈,原來棧幀在0x7ffffffee2a0這個地方,那么我們的數組arr在哪里呢?別著急,這條指令會告訴我們答案:
0x000000000040052a <+4>: movl $0x64,-0x20(%rbp)
這行指令的含義是說把100(0x64)放到rbp寄存器減去0x20的地方,顯然這就是數組的開頭,讓我們來計算一下rbp寄存器減去0x20:
0x7ffffffee2a0(%rbp) - 0x20 = 0x7ffffffee280
因此,我們預測arr應該在0x7ffffffee280這個位置上。
接下來我們用gdb驗證一下:
(gdb) p &arr
$2 = (int (*)[6]) 0x7ffffffee280
哈哈,怎么樣,是不是和我們猜想的一樣,數組arr的確就放在了0x7ffffffee280這個位置,是這樣存儲的:
這就是C語言中所謂的數組了, 無非就是從0x7ffffffee280 到 0x7ffffffee298這一段內存嘛 ,數組在棧區就是這么表示的!
數組與全局區
同樣看一段代碼:
int global_array[6];
void arr_on_global() {
global_array[0]=1;
global_array[1]=2;
global_array[2]=3;
global_array[3]=4;
global_array[4]=5;
global_array[5]=6;
int b = global_array[0];
}
同樣使用# gcc -g -fno-stack-protector a.c編譯,然后用gdb加斷點在int b = global_array[0]這行代碼,看下全局變量global_array的內存位置:
(gdb) p &global_array
$12 = (int (*)[6]) 0x601050
gdb告訴我們數組global_array存放在內存0x601050這個地址上。
注意0x601050這個地址和剛才看到的0x7ffffffee280這個地址相去甚遠,為什么呢?
再看下開局那張圖:
全局區幾乎在最底部,棧區在最頂部,所以相差很遠。
接下來讓我們看看0x601050這個內存區域中到底保存了些啥?
我們使用命令x/6wd 0x601050,這個命令告訴gdb從0x601050這個位置開始以32bit為單位用10進制依次打印6次,讓我們來看看打印的是什么?
(gdb) x/6wd 0x601050
0x601050 <global_array>: 1 2 3 4
0x601060 : 5 6
哈哈,怎么樣,是不是正是全局變量global_array中存放的內容:
這就是C語言中所謂的數組了, 無非就是從 0x601050到 0x601068這一段內存嘛 ,數組在全局區就是這么表示的!
數組與堆區
來段代碼:
void array_on_heap() {
int* arr = (int*)malloc(sizeof(int) * 6);
arr[0] = 100;
arr[1] = 200;
arr[2] = 300;
arr[3] = 400;
arr[4] = 500;
arr[5] = 600;
int a = arr[0];
}
使用gdb加斷點在int a = arr[0];這行代碼,然后打印數組arr的地址:
(gdb) p arr
$20 = (int *) 0x602010
注意0x602010這個地址,這個地址和剛才的全局數組global_array的地址0x601050比較接近,因為堆區和全局區挨得比較近,可以再回過頭看一下開局那張圖。
然后我們同樣使用x命令查看這個區域的內存內容:
(gdb) x/6wd 0x602010
0x602010: 100 200 300 400
0x602020: 500 600
依然不出我們所料,這個區域保存的正是數組的值。
這就是C語言中所謂的數組了, 無非就是從 0x602010到 0x602028這一段內存嘛 ,數組在堆區就是這么表示的!
現在你應該明白了吧,C語言中所謂的數組是怎么表示的?很簡單,其實也沒啥表示, 無非就是內存中一段連續的空間 ,僅此而已。
希望這篇文章對大家理解C語言中的數組有所幫助。
-
內存
+關注
關注
8文章
3099瀏覽量
74847 -
C語言
+關注
關注
180文章
7624瀏覽量
139511 -
數組
+關注
關注
1文章
419瀏覽量
26269
發布評論請先 登錄
相關推薦
評論