C言語の配列を理解する。
C言語の配列宣言が、バイナリレベルでは全く違う事がわかってきた。次は配列とポインタの違いを探るために、array[i]と*(array + i)の違いを探ってみる。
#include <stdio.h> #include <stdlib.h> #define SIZE 5 int main(void) { int i; int array[SIZE] = {0, 1, 2, 3, 4}; for (i = 0; i < SIZE; i++) printf("%d ", array[i]); puts(""); for (i = 0; i < SIZE; i++) printf("%d ", *(array + i)); puts(""); exit(EXIT_SUCCESS); }
出力されるテキストは変わらない。
gcc -gだけ付けてコンパイルして、objdump -dSで、printf部分を抜き出してみた。
printf("%d ", array[i]); 80483f1: 8b 45 f8 mov 0xfffffff8(%ebp),%eax 80483f4: 8b 44 85 e4 mov 0xffffffe4(%ebp,%eax,4),%eax 80483f8: 89 44 24 04 mov %eax,0x4(%esp) 80483fc: c7 04 24 40 85 04 08 movl $0x8048540,(%esp) 8048403: e8 cc fe ff ff call 80482d4 <printf@plt> printf("%d ", *(array + i)); 8048427: 8b 45 f8 mov 0xfffffff8(%ebp),%eax 804842a: c1 e0 02 shl $0x2,%eax 804842d: 89 c2 mov %eax,%edx 804842f: 8d 45 e4 lea 0xffffffe4(%ebp),%eax 8048432: 01 d0 add %edx,%eax 8048434: 8b 00 mov (%eax),%eax 8048436: 89 44 24 04 mov %eax,0x4(%esp) 804843a: c7 04 24 40 85 04 08 movl $0x8048540,(%esp) 8048441: e8 8e fe ff ff call 80482d4 <printf@plt>
0xffffffe4(%ebp,%eax,4),%eax なぬぅぅぅ。こんな書き方出来るのか・・・。勉強になった。
GCCの標準では全くといっていいほど最適化は行われていないらしい。
array[i]の方が断然早い。
と、結論づけようとしたが、-O2で最適化した場合、全く同じバイナリが出てきた。
80483fe: 66 90 xchg %ax,%ax for (i = 0; i < SIZE; i++) printf("%d ", array[i]); 8048400: 8b 44 9e fc mov 0xfffffffc(%esi,%ebx,4),%eax 8048404: 83 c3 01 add $0x1,%ebx 8048407: c7 04 24 40 85 04 08 movl $0x8048540,(%esp) 804840e: 89 44 24 04 mov %eax,0x4(%esp) 8048412: e8 bd fe ff ff call 80482d4 <printf@plt> 8048417: 83 fb 06 cmp $0x6,%ebx 804841a: 75 e4 jne 8048400 <main+0x40>
ループごとまとめて最適化された。*(array + i)も同様の結果になった。
「GCCは最適化オプションを有効にしないとまともな最適化はしない」