strncmp
はstrcmp
よりも通常推奨されるようですが、どのような利点がありますか?セキュリティに関係していると思います。これが事実である場合、入力文字列の1つが"LiteralString"
のようにリテラル定数であることがわかっている場合でも引き続き適用できますか?
PDATE:つまり、文字列全体を比較する必要がある同じユーザーシナリオの下で、strncmp
を以下のように使用できます。それが理にかなっているのではないかと思います。
strncmp(inputString, "LiternalString", strlen("LiternalString"));
strcmp
の問題は、誤って渡された引数が有効なC文字列ではない場合があることです(つまり、p1またはp2がnull文字で終了していない、つまり NULL終了文字列ではない) )の場合、アクセスできないメモリに到達してクラッシュするか、予期しない動作が発生するまで、strcmpは比較を続けます。
strncmp
を使用すると、検索を制限して、アクセスできないメモリに到達しないようにすることができます。
しかし、そのことからstrcmp
を使用するのは安全でないと結論付けるべきではありません。どちらの機能も、意図したとおりに機能します。プログラマは、その関数を使用する前にその関数のman
ページを読み取る必要があり、そのようなライブラリ関数にパラメータを渡す場合は十分に誠実でなければなりません。
ほぼ同様の質問を含む [〜#〜] this [〜#〜] を読むこともできます。
strncmp
には「strcmp
よりも優れている点」はありません。むしろ、彼らはさまざまな問題を解決します。 strcmp
は、2つの文字列が等しいかどうかを判断するためのものです(等しくない場合は、互いに対して順序付け/ソートする方法を考えてください)。 strncmp
は、(主に)文字列が特定の接頭辞で始まるかどうかを決定するためのものです。例えば:
if (strncmp(str, "--option=", 9)==0)
str
が"--option="
で始まるかどうかを判別します。これは、チェックする文字列を変更する(有効な操作ではない可能性があります)か、文字列の無用なコピーを作成しない限り、strcmp
では実現できません。また、memcmp
が少なくとも9バイトの長さのオブジェクトを指していることがわかっている場合を除き、str
を使用してこれを実現することはできません。そうでない場合、memcmp
の呼び出しは未定義の動作になります。
strncmp
には、C以外の文字列データの操作など、他の使用例もあります。
int ret1, ret2;
char dev1[] = { "ABC" };
char dev2[4] = { "ABC" };
dev2[3] = 'D';
ret1 = strncmp(dev1,dev2,strlen(dev1)); // # of characters
ret2 = strncmp(dev1,dev2,sizeof(dev1)); // # of characters plus '\0'
前提:dev1はnullで終了し、dev2は終了していない可能性があります。 ret2 = -1(有効な結果)ではなく、ret1 = 0(偽陽性の結果)
fazit:strncmpはstrcmpの安全な方法ではありません。それをどのように利用するかによります。
文字列にstrcmpを使用し、部分文字列検索にstrncmpを使用します。
ここで反strncmpの使用例を投稿してください。次のコードについて考えてみましょう。
#include <stdio.h>
#include <dirent.h>
int main()
{
//I want to list all files under current folder, include hidden files.
DIR *dir;
struct dirent *dp;
char * file_name;
dir = opendir(".");
while ((dp=readdir(dir)) != NULL) {
printf("debug: %s\n", dp->d_name);
if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") )
//It doesn't work if you replace strcmp with strncmp here.
//if ( !strncmp(dp->d_name, ".", 1) || !strncmp(dp->d_name, "..", 2) )
{
} else {
file_name = dp->d_name;
printf("file_name: \"%s\"\n",file_name);
}
}
closedir(dir);
return 0;
}
... whole strings を比較する必要がある同じユーザーシナリオの下で...
_ strncmp(inputString, "LiternalString", strlen("LiternalString"));
_
文字列inputString
が文字列リテラルよりも長い場合を考えます。
_const char *inputString = "LiternalString_XYZ";
int cmp = strncmp(inputString, "LiternalString", strlen("LiternalString"));
printf("%d\n", cmp); // prints 0.
_
しかし、OPは whole strings を比較したかったのです。
_const char *inputString = "LiternalString_XYZ";
int cmp = strcmp(inputString, "LiternalString");
printf("%d\n", cmp); // prints non-zero.
_
OPの直接的な質問に対する結論
私はそれが理にかなっているかどうか疑問に思っています。
いいえ。全体の文字列を比較するために、strncmp()
は常に正しい結果を提供できません。 strcmp()
を使用します。
さらに
strncmp(s1,s2,n)
は検索を制限できるため、アクセスできないメモリに到達しません if n
が適切に計算されました-これはOPのコードと_s1
_と_s2
_で最高のn
が異なる場合に正しく実行するのに問題があります。
サイズ制限n
は_s1
_と_s2
_の両方に適用されるため、この関数はプレフィックスの比較に役立ちますが、「安全」ではありません。
OPの場合、文字列リテラル文字列リテラルの文字列長のため、「安全」のために検索を制限する理由はありません。 always には、終端のnull characterが含まれています。
どちらかといえば、n
はinputString
のプロパティに基づいているはずです。
strncmp(s1, string_literal, strlen(s1))
のようなコードは、比較でnull文字が欠落しているため、機能的に正しくありません。少し良いのはstrncmp(s1, string_literal, strlen(s1)+1)
ですが、機能的には単純なstrcmp(s1, string_literal)
と同じですが、「安全性」は低下しません。
以下は、foo()
がstringsを正しく形成しなかった場合の「安全性」の改善を提供しますが、上記のように_N != M
_の場合、間違った答えを提供する可能性があります。
_char s1[N];
foo(s1); // populate s1 somehow
int cmp = strncmp(s1, string_literal, sizeof s1);
char s1[N];
char s2[M];
foo(s1); // populate s1 somehow
foo(s2); // populate s2 somehow
int cmp = strncmp(s1, s2, min(sizeof s1, sizeof s2));
_
しかし、それらの場合、問題はここではなくfoo()
にあります。
私にとって、foo()
が疑わしい場合は、以下を使用します
_s1[sizeof s1 - 1] = '\0';
s2[sizeof s2 - 1] = '\0';
int cmp = strcmp(s1, s2);
_
または、foo()
がstringを形成しなかったことを検出します。
_char s1[N];
foo(s1);
if (memchr(s1, '\0', sizeof s1) == NULL) Oops();
_
ストーリーのモラル:strncmp()
はstrcmp()
の「安全な」バージョンではありません。それは別の仕事のためのツールです:文字列プレフィックスの比較。