C言語 ポインタ同士の引き算
アドレスの差分にはならない.
int main(void){ int a[2]; int k = &a[1] - &a[0]; return 0; }
上のコードを実行したら, kを表示すると結果は1となる. 4ではない. ポインタ同士の引き算は内部でアドレスの値を引いた後にそのポインタが指している変数の型のバイト数(sizeof(変数の型))で割った結果を求めるようにコンパイラは働く.
実際に上のコードをコンパイルしたアセンブラを見ると,
0000000100000f9e <_main>: 100000f9e: 55 push rbp 100000f9f: 48 89 e5 mov rbp,rsp 100000fa2: c7 45 fc 01 00 00 00 mov DWORD PTR [rbp-0x4],0x1 //変数kに1が代入されている 100000fa9: b8 00 00 00 00 mov eax,0x0 100000fae: 5d pop rbp 100000faf: c3 ret
のようになっている. これは最適化を切ってコンパイルした結果である.
連続領域の場合最適化を切っていても, コンパイル時に相対距離が計算できるので, コンパイル時に計算してしまうようだ(下記の結果から推測).
次に, 連続領域に存在しないポインタの引き算を実行してみる.
int main(void){ int a[2], b[2]; int k = &b[0] - &a[0]; return 0; }
上のコードを最適化を切って, コンパイルしたアセンブラは以下のようになる.
0000000100000f90 <_main>: 100000f90: 55 push rbp 100000f91: 48 89 e5 mov rbp,rsp 100000f94: 48 8d 55 e0 lea rdx,[rbp-0x20] // rdx に &b[0]を代入 100000f98: 48 8d 45 f0 lea rax,[rbp-0x10] // rax に &a[0]を代入 100000f9c: 48 29 c2 sub rdx,rax // rdxからraxを引く 100000f9f: 48 89 d0 mov rax,rdx // rdxの値をraxに代入 100000fa2: 48 c1 f8 02 sar rax,0x2 //raxの値を右へシフト2する (rax /= 4) 100000fa6: 89 45 fc mov DWORD PTR [rbp-0x4],eax //変数kに代入する 100000fa9: b8 00 00 00 00 mov eax,0x0 100000fae: 5d pop rbp 100000faf: c3 ret
今回はコンパイル時には計算できないようで, sizeof(int)で割る処理が行なわれている(sar rax,0x2).
double 型でも同様なことをやっておく.
int main(void){ double a, b; int k = &a - &b; //1 int l = &b - &a; //-1 return 0; }
0000000100000f7b <_main>: 100000f7b: 55 push rbp 100000f7c: 48 89 e5 mov rbp,rsp 100000f7f: 48 8d 55 f0 lea rdx,[rbp-0x10] // rdx = &a; 100000f83: 48 8d 45 e8 lea rax,[rbp-0x18] // rax = &b; 100000f87: 48 29 c2 sub rdx,rax // rdx -= rax; 100000f8a: 48 89 d0 mov rax,rdx // rax = rdx; 100000f8d: 48 c1 f8 03 sar rax,0x3 // rax >>= 3; (rax /= sizeof(double)) 100000f91: 89 45 fc mov DWORD PTR [rbp-0x4],eax // k = eax; 100000f94: 48 8d 55 e8 lea rdx,[rbp-0x18] // rdx = &b; 100000f98: 48 8d 45 f0 lea rax,[rbp-0x10] // rax = &a; 100000f9c: 48 29 c2 sub rdx,rax // rdx -= rax; 100000f9f: 48 89 d0 mov rax,rdx // rax = rdx; 100000fa2: 48 c1 f8 03 sar rax,0x3 // rax >>= 3 (rax /= sizeof(double)) 100000fa6: 89 45 f8 mov DWORD PTR [rbp-0x8],eax // l = eax; 100000fa9: b8 00 00 00 00 mov eax,0x0 // return 0;を設定 100000fae: 5d pop rbp 100000faf: c3 ret
上記はコードとそのアセンブラの結果である.
sar rax,0x3
という命令があり, 右へ3シフトする. つまり sizeof(double) で割っているということ.
実際のポインタの値同士の引き算をするには?
int main(void){ int a[2]; int k = (int)&a[1] - (int)&a[0]; return 0; }
上のコードのkには4が代入される. int型にキャストしてから演算すればいい.