でかっ
こういうとき便利だ.
void strcpy2C(char *s, const char *t)
{
int i = 0;
int j = 0;
while ((s[i++] = t[j++]) != '\0')
;
}
が正しい. これはVC6 -O2にて
mov eax, [esp+4] ; s
mov edx, [esp+8] ; t
sub edx, eax
.lp:
mov cl, [edx+eax]
mov [eax], cl
inc eax
test cl, cl
jne .lp
ret
となった.
泥臭い最適化はかかってないが,
iとjが同時にインクリメントされるということがきちんと認識されている.
1Aの
mov edx, [esp+8] ; t
mov ecx, [esp+4] ; s
.lp:
mov al, [edx]
mov [ecx], al
inc ecx
inc edx
test al, al
jne .lp
ret
と比べても素直で特に問題ないコード.
つまり, 議論の発端は1Aと次の1B
void strcpy1B(char *s, const char *t)
{
while (*s = *t) {
s++;
t++;
}
}
を比較すべき話であった.
そして昨日書いたように二つの差はループにおいてはどう書いても殆ど変わらない.
ま, もっと複雑な例ではこのような微妙な表現を変えていくと結構速度が変わるあたり,
まだまだ甘いなあとは思う. そしてその辺が飯の種になったりするわけやけど(というかなって!).
void strcpy1A(char *s, const char *t)
{
while (*s++ = *t++)
;
}
はループの回数だけ必ずsやtはインクリメントされるけど
void strcpy2A(char *s, const char *t)
{
int i = 0;
while ((s[i] = t[i]) != '\0')
i++;
}
のiはそうじゃない(t[0] == 0ならi = 0). だからこの文が続いたときの挙動は異なる(のが異なる要因となっている).
strcpy1Aと比較するなら
void strcpy2B(char *s, const char *t)
{
int i = 0;
while ((s[i] = t[i++]) != '\0')
;
}
で, これならループ自体は1Aよりも1命令少ないコードが生成された(gcc 4.0.2, VC6, VC8).
mov edx, DWORD PTR [esp+8]
mov ecx, DWORD PTR [esp+4]
.lp:
mov al, BYTE PTR [edx]
mov BYTE PTR [ecx], al
add ecx, 1
add edx, 1
test al, al
jne SHORT .lp
ret
で2Aは
mov edx, DWORD PTR [esp+8]
mov cl, BYTE PTR [edx]
test cl, cl
mov eax, DWORD PTR [esp+4]
mov BYTE PTR [eax], cl
je .exit
sub edx, eax
.lp:
mov cl, BYTE PTR [edx+eax+1]
add eax, 1
test cl, cl
mov BYTE PTR [eax], cl
jne SHORT .lp
.exit:
ret
だから最近のPentiumならあんまし変わんないかもしれないけどクラシックPentiumに関しては2Aの方がループ処理が高速な分優秀だ.