warning: ‘%s’ directive argument is null
[-Wformat-overflow=]
■この記事の概要
この記事では、C言語のprintf
で書式指定子%s
にNULL
ポインタを渡すと未定義動作となる問題を解説しています。(null)
と表示されるケースもありますが、セグメンテーションフォルトが発生することもあるため、NULL
を渡さないようにする対策が推奨されています。例えば、戻り値チェックを徹底することで安全性を確保できます。
■書式%sにNULLポインタが渡るとSegmentation fault
//puts()に置き換えれる時だけ落ちる
// printf("%s\n",NULL)->puts(NULL)
#include <stdio.h>
int main(void){
//以下未定義動作
printf("前[%s]\n", NULL); fflush(NULL);
printf("[%s]後\n", NULL); fflush(NULL);
printf("[%s]", NULL); fflush(NULL);
printf("%s\t", NULL); fflush(NULL);
//落ちる
printf("%s\n", NULL); fflush(NULL);
}
./a.out
前[(null)]
[(null)]後
[(null)](null) Segmentation fault
Windows系のコンパイラでは書式%sにNULLポインタが渡ると
“(null)”と表示されるそうですが、
Linux系(gcc,clang)では
コンパイラと最適化レベルで結果が異なりました。
”(null)”と表示されたり
Segmentation faultと表示して異常終了したりします。
推測ですがgccの場合printf自体には%sにNULLが来ても(null)と表示する機能があるのですが
printf(“%s\n”,NULL)➡puts(NULL)の最適化が行われると異常終了するようです。
いずれにせよ未定義動作なので
“%s”にNULLが渡らないようにするのが賢明です。
■戻り値をチェックしないでNULLポインタとなる例
#include <string.h>
#include <stdio.h>
int main(void){
const char *cp = "abc,def,ghi";
printf("%s\n",cp);
cp = strchr(cp,',');
printf("%s\n",cp);
fflush(stdout);
cp = strchr(cp+1,',');
printf("%s\n",cp);
fflush(stdout);
cp = strchr(cp+1,',');
printf("%s\n",cp);
fflush(stdout);
}
./a.out
abc,def,ghi
,def,ghi
,ghi
Segmentation fault (コアダンプ)
書式%sにNULLポインタを渡すのはC言語規格外の未定義動作なので
職業プログラマはきちんとstrchr()の返却値をチェックして
書式%sにNULLポインタが渡らないようにチェックしましょう。
参考:
FIO47-C. 書式指定文字列を正しく使う
https://www.jpcert.or.jp/sc-rules/c-fio47-c.html