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は最適化オプションを有効にしないとまともな最適化はしない」