2012年10月5日 星期五

c pointer vs array and they are different

test environment: 32bit code, in bootloader

array 和 pointer 有什麼不同呢?C 专家编程 (Expert C Programming) 裡頭有不少的說明。我要提到的是另外一個觀察。

cb.c
 1 __asm__(".code16gcc\n");

 5 
 6 void WinMain(void)
 7 {
 8   char arr[]="abcdefghi";
 9   char *arrp = arr;
10   *arrp = 'z';
11 
12   while(1);
13 }

objdump -d -m i8086 cb.elf
 3 cb.elf:     file format elf32-i386
 4 
 5 
 6 Disassembly of section .text:
 7 
 8 00007c00 <WinMain>:
 9     7c00:       66 55                   push   %ebp
10     7c02:       66 89 e5                mov    %esp,%ebp
11     7c05:       66 83 ec 10             sub    $0x10,%esp
12     7c09:       67 66 c7 45 f2 61 62    movl   $0x64636261,-0xe(%ebp)
13     7c10:       63 64 
14     7c12:       67 66 c7 45 f6 65 66    movl   $0x68676665,-0xa(%ebp)
15     7c19:       67 68 
16     7c1b:       67 c7 45 fa 69 00       movw   $0x69,-0x6(%ebp)
17     7c21:       67 66 8d 45 f2          lea    -0xe(%ebp),%eax
18     7c26:       67 66 89 45 fc          mov    %eax,-0x4(%ebp)
19     7c2b:       67 66 8b 45 fc          mov    -0x4(%ebp),%eax
20     7c30:       67 c6 00 7a             movb   $0x7a,(%eax)
21     7c34:       eb fe                   jmp    7c34 <WinMain+0x34>

我透過作業系統之前的程式 (這樣可以簡化), 使用 bochs 內建除錯器 single step 跑了一次, 終於有了深刻的印象。

下圖是我將記憶體的內容 (stack) 根據程式畫出來, 對照著反組譯的 code, 應該很好理解。

17     7c21:       67 66 8d 45 f2          lea    -0xe(%ebp),%eax

把 -0xe(%ebp) 的位址 (ffc4) 抓出來。

arr 本身就代表某個位址, 就是 ffc4, 而在 c 語言用 &arr, arr 都是得到  ffc4。
arrp 本身代表某個位址 (ffce), 而這個位址用來存一個位址 0000ffc4。 &arrp 得到 ffce, arrp 則是 ffc4, 那 *arrp 呢?自然就是從 ffc4 抓一個 byte 的值, 就是 0x61 = 'a'。

%epb = 0xffd2 這是系統的 epb 值, 在不同平台上有可能會不同。

ffd2
00
00
ff
c4ffce -4(%ebp) arrp
00
69ffcc -6(%ebp)
68
67
66
65ffc8 -a(%epb)
64
63
62
61ffc4 -e(%epb) arr

編譯方式:
gcc -fno-stack-protector -std=c99 -m32 -ffreestanding -Wall -g -c cb.c
ld -m elf_i386 -static -Tl.ld -nostdlib -M -o cb.elf cb.o > cb.elf.map
objcopy -R .pdr -R .comment -R.note -S -O binary cb.elf cb.bin

這個簡單的組合語言程式可花了我不少腦力

這是另外一個有趣的問題 (作者: sunlights (sunlights) 看板: C_and_CPP標題: [問題] 指標和陣列的問題)

cb1.c
 1 __asm__(".code16gcc\n");

 8 int main(void)
 9 {
10   char arr[]="abcdefghi";
11   char *arrp = arr;
12   int addr = &arr+1;

22   while(1);
23 }

objdump -d -m i8086 cb.elf
 3 cb.elf:     file format elf32-i386
 4 
 5 
 6 Disassembly of section .text:
 7 
 8 00007c00 <main>:
 9     7c00:       66 55                   push   %ebp
10     7c02:       66 89 e5                mov    %esp,%ebp
11     7c05:       66 83 ec 20             sub    $0x20,%esp
12     7c09:       67 66 c7 45 ee 61 62    movl   $0x64636261,-0x12(%ebp)
13     7c10:       63 64 
14     7c12:       67 66 c7 45 f2 65 66    movl   $0x68676665,-0xe(%ebp)
15     7c19:       67 68 
16     7c1b:       67 c7 45 f6 69 00       movw   $0x69,-0xa(%ebp)
17     7c21:       67 66 8d 45 ee          lea    -0x12(%ebp),%eax
18     7c26:       67 66 89 45 f8          mov    %eax,-0x8(%ebp)
19     7c2b:       67 66 8d 45 ee          lea    -0x12(%ebp),%eax
20     7c30:       66 83 c0 0a             add    $0xa,%eax
21     7c34:       67 66 89 45 fc          mov    %eax,-0x4(%ebp)
22     7c39:       eb fe                   jmp    7c39 <main+0x39>

12   int addr = &arr+1;

會怎樣?
組合語言面前沒有秘密, 似乎就是這行:

20     7c30:       66 83 c0 0a             add    $0xa,%eax

我沒有答案, 為什麼該 C 語言 +1 的動作在組合語言變成 + 0xa, 不懂?

若是改為
&arr+2;
&arr+3;
add    $0x14,%eax
add    $0x1e,%eax 
每加 1, 組合語言則是 + 0xa

難道這是未定義之行為嗎?
推 LPH66:你的 &arr 其型態是 char (*)[10] 因此 +1 時會跳十格        10/05 09:52
→ LPH66:之所以是十格是因為 sizeof(char[10]) == 10 的關係          10/05 09:52
感謝 LPH66, 一語驚醒夢中人。
arr+1
&arr+1
行為很不同, 注意別寫錯了。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。