web-dev-qa-db-ja.com

strncmpに対するstrncmpの利点は?

strncmpstrcmpよりも通常推奨されるようですが、どのような利点がありますか?セキュリティに関係していると思います。これが事実である場合、入力文字列の1つが"LiteralString"のようにリテラル定数であることがわかっている場合でも引き続き適用できますか?

PDATE:つまり、文字列全体を比較する必要がある同じユーザーシナリオの下で、strncmpを以下のように使用できます。それが理にかなっているのではないかと思います。

strncmp(inputString, "LiternalString", strlen("LiternalString"));
11
Thomson

strcmpの問題は、誤って渡された引数が有効なC文字列ではない場合があることです(つまり、p1またはp2がnull文字で終了していない、つまり NULL終了文字列ではない) )の場合、アクセスできないメモリに到達してクラッシュするか、予期しない動作が発生するまで、strcmpは比較を続けます。

strncmpを使用すると、検索を制限して、アクセスできないメモリに到達しないようにすることができます。

しかし、そのことからstrcmpを使用するのは安全でないと結論付けるべきではありません。どちらの機能も、意図したとおりに機能します。プログラマは、その関数を使用する前にその関数のmanページを読み取る必要があり、そのようなライブラリ関数にパラメータを渡す場合は十分に誠実でなければなりません。

ほぼ同様の質問を含む [〜#〜] this [〜#〜] を読むこともできます。

20

strncmpには「strcmpよりも優れている点」はありません。むしろ、彼らはさまざまな問題を解決します。 strcmpは、2つの文字列が等しいかどうかを判断するためのものです(等しくない場合は、互いに対して順序付け/ソートする方法を考えてください)。 strncmpは、(主に)文字列が特定の接頭辞で始まるかどうかを決定するためのものです。例えば:

if (strncmp(str, "--option=", 9)==0)

str"--option="で始まるかどうかを判別します。これは、チェックする文字列を変更する(有効な操作ではない可能性があります)か、文字列の無用なコピーを作成しない限り、strcmpでは実現できません。また、memcmpが少なくとも9バイトの長さのオブジェクトを指していることがわかっている場合を除き、strを使用してこれを実現することはできません。そうでない場合、memcmpの呼び出しは未定義の動作になります。

strncmpには、C以外の文字列データの操作など、他の使用例もあります。

それはすべてあなたのユースケースに依存します。固定数の文字のみを比較する必要がある場合は strncmp を使用し、文字列全体を比較する必要がある場合は strcmp を使用します。

それだけです。

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を使用します。

1
user9455050

ここで反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;
}
1
user3099889

... 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が含まれています。

どちらかといえば、ninputStringのプロパティに基づいているはずです。

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()の「安全な」バージョンではありません。それは別の仕事のためのツールです:文字列プレフィックスの比較。