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) を返す自作関数を作ってはいけません
参考: