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