はい、あなたのチェックは正しいですが、確かに最も効率的ではありません (「効率」が計算効率を意味する場合)。実装における明らかに直感的な非効率性は、文字列が実際にオーバーラップすると、呼び出しが共通部分を 2 回strlen
反復するという事実に基づいています。
正式な効率のために、わずかに異なるアプローチを使用する場合があります
int are_overlapping(const char *a, const char *b)
{
if (a > b) /* or `(uintptr_t) a > (uintptr_t) b`, see note below! */
{
const char *t = a;
a = b;
b = t;
}
while (a != b && *a != '\0')
++a;
return a == b;
}
このバージョンに関する重要な注意事項は、同じ配列を指していることが保証されていない 2 つのポインターの関係比較を実行することです。これは正式には未定義の動作につながります。これは、フラット メモリ モデルのシステムで実際に動作しますが、衒学的なコード レビュー担当者から批判を受ける可能性があります。この問題を正式に回避するには、uintptr_t
リレーショナル比較を実行する前にポインターを に変換します。このようにして、未定義の動作は、フラット メモリ モデルを使用するほとんどの (すべてではないにしても) 従来の実装で、適切なセマンティクスを使用して実装定義の動作に変換されます。
このアプローチには、「二重カウント」の問題がありません。メモリ内の「前」にある文字列の重複しない部分のみを分析します。もちろん、実際には、このアプローチの利点は存在しないことが判明する可能性があります。strlen
それは、実装の品質と実際の入力のプロパティの両方に依存します。
たとえば、この状況では
const char *str = "Very very very long string, say 64K characters long......";
are_overlapped(str, str + 1);
私のバージョンは、あなたのバージョンよりもはるかに速くオーバーラップを検出します。私のバージョンはサイクルの 1 回の反復でそれを行いますが、あなたのバージョンは 2 * 64K の反復を費やします ( の単純な実装を想定strlen
)。
疑わしいポインター比較の領域に飛び込むことにした場合は、上記のアイデアを次のように再実装することもできます。
int are_overlapping(const char *a, const char *b)
{
if (a > b)
{
const char *t = a;
a = b;
b = t;
}
return b <= a + strlen(a);
}
この実装は、反復ごとに追加のポインター比較を実行しません。そのために支払う代償は、早期に終了するのではなく、常に文字列の 1 つの終わりまで反復することです。それでも、呼び出しstrlen
は 1 回だけなので、実装よりも効率的です。