【C言語】
strcmp()の基本的な使い方と
避けたい使い方

warning: function ‘strcmp’ is compared to a suspicious constant

警告:strcmpの戻り値を0以外と比較するのは疑わしい
[misc-suspicious-string-compare]


■この記事の概要

この記事では、C言語のstrcmp関数の戻り値の誤解を解説しています。戻り値はtrue/falseではなく、文字列の差分を表します。誤用例や効率的な文字列比較手法、NULLポインタの扱い方、非推奨な自作関数のリスクも取り上げ、安全なコードを書くための注意点を具体的に説明しています。


■strcmpの基本的な使い方

C言語ではif(pointer == “abc”)のような文字列比較はできません。
コンパイルはできますが未規定の動作となり
プログラマの意図した動きをしません。

文字列を比較するときは
strcmp関数を使用します。
strcmpはストリングコンペアの略です。

以下にstrcmpの基本的な使い方を示します

#include <stdio.h>
#include <string.h>//strcmpのプロトタイプ宣言
void f(char *s1,char *s2){
    int ret = strcmp(s1,s2);
    if(ret < 0){
        printf("%sは%sより辞書順で先\n",s1,s2);
    } else if(ret > 0){
        printf("%sは%sより辞書順で後\n",s1,s2);
    } else {
        printf("%sと%sは同じ\n",s1,s2);
    }
}
int main(void){
    f("AAA","BBB");
    f("AAA","AAA");
    f("BBB","AAA");
}
./a.out
AAAはBBBより辞書順で先
AAAとAAAは同じ
BBBはAAAより辞書順で後

strcmpの戻り値は第1引数と第2引数の差分です。
第1引数と第2引数が等しい時、
差分無し、すなわち0が返ります。

■strcmpでNULLポインタ判定はできない

warning: argument 2 null where non-null expected
[-Wnonnull]

#include <stdio.h>
#include <string.h>
void f(char *p){
    if(strcmp(p,NULL) == 0){
        puts("ここに来る前に異常終了する?");
        return  ;
    }
    puts(p);
}
int main(void){
    f("有効ポインタ");
    f(NULL);
}

strcmpにNULLを渡すと
NULLポインタ逆参照が発生するので
筆者の環境下ではSegmentation fault (コアダンプ)となりました。

■空文字列判定の一文字比較にstrcmpは非推奨

//空文字列判定
#include <stdio.h>
#include <string.h>
void NG(char *p){
    if(strcmp(p,"") == 0){//非推奨
        puts("効率が悪い");
    }
}
void OK(char *p){
    if(p[0] == '\0'){   //推奨
        puts("効率が良い");
    }
}
int main(void){
    NG("");
    OK("");
}

strcmp(pointer,””)で空文字列の判定ができますが
記述量が多く、関数コールのオーバーヘッドで遅くなるので
pointer[0] == ‘\0’と
一文字比較を推奨します。


■strcmpの戻り値はtrue/falseではない

#include <stdbool.h>
#include <stdio.h>
#include <string.h>
int main(void){
    char *s1 = "abc";
    char *s2 = "abc";
    
    if (strcmp(s1,s2) == true)     
        puts("同じ");  
    else                         
        puts("違う");
}

strcmpの戻り値はtrue/falseではありません。
マニュアルを読みましょう。


■strcmpで0と比較するのを忘れる

(warning): The expression ‘strcmp(p,”def”) != 0’ is suspicious. It overlaps ‘strcmp(p,”abc”) == 0’.

警告:第2条件で==0が抜けているかも[(warning)overlappingStrcmp]

#include <stdio.h>
#include <string.h>
int main(void) {
    char    *p = "def";

    if(strcmp(p,"abc") == 0 || strcmp(p,"def"))
        puts("ここには来ない");
    else    
        puts("ここに来る");
}

strcmp()は第1、第2引数が同じ時0を返しますが、
うっかり0と比較するのを忘れる場合があります。


■strcmpと似た関数を作ってはいけない

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
//文字列は同じか?
int IsStringSame(const char *s1,const char *s2){
    int i;
    for(i = 0;s1[i] == s2[i];i++){
        if(s1[i] == '\0'){
            return  true;//同じ
        }
    }
    return  false ;//違う
}
int main(void){
    if(IsStringSame("abc","abc") == true) 
        puts("同じ");
    else                                  
        puts("違う");
    
    //strcmpに似た関数を作ると
    //strcmpに似た使い方をする者が現れる    
    if(IsStringSame("abc","abc") == 0) 
        puts("同じ");
    else                                  
        puts("違う");
}

strcmp()と似た関数を作る者が現れると
strcmp()と似た使い方をする者が必ず現れる(←つまりバグる)


参考:

C-FAQ17.3:ほら、このわざを見てif(!strcmp(s1, s2))

EXP20-C. 成功、真偽、等価を判定するには明示的な検査を行う