【C言語】
strcmp()の戻り値はtrue/falseではない

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

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

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


■strcmpとは文字列を比較する関数です

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

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

strcmp関数は
if(strcmp(ptr,”abc”) == 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と比較するのが正しい

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

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


■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()と似た使い方をする者が必ず現れる(←つまりバグる)


■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("有効ポインタ");
}
int main(void){
    f(NULL);
}

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

■strcmpで空文字列判定できるが効率が悪い

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

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

■まとめ

●if(strcmp(s1,s2)==1は多分バグ
●if(strcmp(s1,s2)== 0)の誤解しやすい読み方
「もしs1とs2の比較結果が偽(0)ならば」
●if(strcmp(s1,s2)== 0)の推奨する読み方
「もしs1とs2の差分が無(0)ければ」
●文字列が等しい時 true(1) を返す自作関数を作ってはいけません

参考:

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

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