warning: ignoring return value of ‘scanf’ declared with attribute ‘warn_unused_result’
警告:属性’warn_unused_result’で宣言された’scanf’の戻り値を無視しています。
[-Wunused-result]
■この記事の概要
この記事では、C言語でscanf
関数の戻り値を確認する重要性を解説しています。書式エラー時の不具合を防ぐため、fgets
とsscanf
の併用を推奨。また、strcpy
の戻り値は無視しても問題ないケースについても説明しています。
■”戻り値が無視されました”の意味
scanf 等の関数は処理に失敗すると
エラーを戻り値で返します。
でもプログラマが戻り値を確認して
エラーメッセージ等を画面に表示しないと、
ユーザーはエラーに気が付きません。
なので親切?なコンパイラは
”戻り値のエラー確認しなくて良いの?”と
警告するわけです。
■警告の出る例
#include <stdio.h>
//scanf(警告あり)
int main(void) {
for(;;){
int age ;
char name[256];
printf("Usage:name age\n");
scanf("%s %d",name,&age);
printf("name=%s age=%d\n",name,age);
}
}
一般的にエラーを返す関数の戻り値をチェックするのが
プロの仕事ですが、
実はscanf関数は特別で戻り値をチェックしても
プログラムはうまく動きません。
scanf関数を使うのは止めて fgets()+sscanf()を使いましょう。
scanf()は
書式に一致し引数に代入が成功した入力要素の個数を返します。
上記コードの場合、
書式変換に成功するとscanf()は2を返却します。(%s と%d の2変換)
書式変換に失敗するとscanf()は2未満の変換に成功した数、またはEOFを返します。
書式変換に失敗した場合、出力引数(name,age) には適切な値は設定されていません。
■推奨しない修正案
#include <stdio.h>
//scanf 戻り値チェック版(警告無し)
int main(void) {
for(;;){
int age ;
char name[256];
printf("Usage:name age\n");
if(scanf("%s %d",name,&age) != 2) {
printf("入力形式が違います");
continue;
}
printf("name=%s age=%d\n",name,age);
}
}
scanfの戻り値をチェックすると、コンパイラの警告は消えます。
しかし入力が正しくない場合、このコードでも暴走してしまい正しくエラー処理ができません。
■推奨する修正案
#include <stdio.h>
//fgets+sscnaf版(推奨)
int main(void){
for(;;){
int age ;
char name[256];
char buf[BUFSIZ];
printf("Usage:name age\n");
if(fgets(buf,BUFSIZ,stdin) == NULL){
break;
}
if(sscanf(buf,"%s %d",name,&age) != 2) {
printf("入力形式が違います");
continue;
}
printf("name=%s age=%d\n",name,age);
}
}
scanfは入力形式に異常があるとうまくエラー処理ができないので
変わりに fgets()+sscanf()を使用する事を推奨します。
■戻り値を無視して良い場合
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
//あれば便利程度の戻り値は無視して良い
char dst[1024];
strcpy(dst,"戻り値は無視して良い");
printf("%s\n",dst);
(void)strcpy(dst,"このキャストは不要です");
printf("%s\n",dst);
}
strcpy()のようにあれば便利程度の戻り値は無視してもかまいません。
熱血に(void)strcpy()とキャストする必要もありません。
静的解析ツールがstrcpy()の戻り値を無視したと冗長警告を出す場合、警告除外リスト(=ホワイトリスト)に登録して、戻り値を無視しても問題ないことを静的解析ツールに教え込めないか検討してみてください。
参考:
https://www.jpcert.or.jp/sc-rules/c-exp12-c.html
C-FAQ 12.19: [前略] scanf()の戻り値を調べるほうが安全だと考え た。けれど、たまに無限ループに入ってしまうようだ。
http://www.kouno.jp/home/c_faq/c12.html#19
C-FAQ 12.20:どうして誰もがscanf()を使わないほうがいいというのか。
http://www.kouno.jp/home/c_faq/c12.html#20