この関数が見つかりました ここ 。これはstrcmp
の実装です。
int strcmp(const char* s1, const char* s2)
{
while (*s1 && (*s1 == *s2))
s1++, s2++;
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
私は最後の行を除いてすべてを理解しています、要するに最後の行で何が起こっているのですか?
_return *(const unsigned char*)s1-*(const unsigned char*)s2;
_
OP:要するに、最後の行で何が起こっているのですか?
A:最初の潜在的な文字列の違いが比較されます。両方のchars
は、仕様で要求されているように_unsigned char
_として参照されます。 2はint
にプロモートされ、差が返されます。
ノート:
1戻り値の符号(<0、0、> 0)が最も意味のある部分です。 C仕様で指定されているのはこれだけです。
2一部のシステムでは、char
はsigned
(より一般的)です。その他の場合、char
はunsigned
です。最後の比較の「符号」を定義すると、移植性が向上します。 fgetc()
は文字を_unsigned char
_として取得することに注意してください。
3文字列が_\0
_で終わることを除いて、使用される文字エンコード(ASCII-最も一般的)など)は、バイナリレベルで違いはありません。最初のchar
sの値は65と97であり、文字エンコードが非ASCIIであっても、最初の文字列は2番目の文字列よりも小さくなります。OTOH、strcmp("A", "a")
は、文字の場合に負の数値を返します。エンコーディングはASCIIですが、基になる値と順序がCで定義されていないため、異なる文字エンコーディングで正の数値を返す場合があります。
この実装は、組み込みのstrcmp
の最適化ではなく、単なる別の実装であり、おそらく組み込みバージョンよりもパフォーマンスが低下すると思います。
比較関数は、比較される値が等しい場合は0を返し、最初の値が小さい場合は負の数を返し、最初の値が大きい場合は正の数を返すことになっています。そして、それが最後の行で起こることです。
最後の行のアイデアは、文字をunsigned charにキャストすることであり、作成者は、これが非標準文字(ASCIIコード0-127)の後に非標準文字をソートすることを意図したと思います。
編集:コードにバグはなく、s1
が指す値が、s2
が指す値よりも小さい場合、コード128以上の文字の前に標準文字を並べ替えると、負の値を返す可能性があります。 。
この実装はさらに最適化でき、いくつかの比較を省きます。
_int strcmp(const char *s1, const char *s2) {
unsigned char c1, c2;
while ((c1 = *s1++) == (c2 = *s2++)) {
if (c1 == '\0')
return 0;
}
return c1 - c2;
}
_
文字列が終了ヌルバイトまで同一である場合、戻り値は_0
_です。戻り値の符号は、最初の異なる文字間の差の符号であり、C標準に従って_unsigned char
_に変換されます。
char
がint
よりも小さい場合(これは一部のまれな組み込みシステムを除くすべてに当てはまります)、この差は_c1
_と_c2
_の両方の単純な減算で計算できます。 int
にプロモートされ、この差はタイプint
の範囲に収まることが保証されています。
sizeof(int) == 1
があるシステムでは、戻り値は次のように計算する必要があります。
_return (c1 < c2) ? -1 : 1;
_
私はこのコードを好みます:
int strcmp(const char *str1, const char *str2)
{
int s1;
int s2;
do {
s1 = *str1++;
s2 = *str2++;
if (s1 == 0)
break;
} while (s1 == s2);
return (s1 < s2) ? -1 : (s1 > s2);
}
aRMv4の場合、次のようにコンパイルされます。
strcmp:
ldrsb r3, [r0], #1 ;r3 = *r0++
ldrsb r2, [r1], #1 ;r2 = *r1++
cmp r3, #0 ;compare r3 and 0
beq @1 ;if r3 == 0 goto @1
cmp r3, r2 ;compare r3 and r2
beq strcmp ;if r3 == r2 goto strcmp
;loop is ended
@1:
cmp r3, r2 ;compare r3 and r2
blt @2 ;if r3 < r2 goto @2
movgt r0, #1 ;if r3 > r2 r0 = 1
movle r0, #0 ;if r3 <= r2 r0 = 0
bx lr ;return r0
@2:
mov r0, #-1 ;r0 = -1
bx lr ;return r0
ご覧のとおり、ループの下には6つの命令しかなく、最後には最大5つの命令があります。したがって、複雑さは6 *(strlen + 1)+5です。
(s1 == 0)をwhile条件に移動すると、ARM(理由はわかりません)のマシンコードが悪化します。
strcmp
は、それらが等しいかどうかだけでなく、どの文字列がより大きい他の文字列であるかを返します。
最後の行は、最初の一致しない文字を差し引いて、どちらが大きいかを確認します。文字列全体が一致する場合は、0-0=0
になり、「等しい」結果が得られます。
この実装は、アセンブリコードとキャッシュライン、ロードサイズなどの知識が必要になるため、あまり最適化されていません。