まず最初に:printf
は %s 引数に有効な (つまり、非 NULL) ポインターを期待しているため、NULL を渡すことは公式には定義されていません。「(null)」と表示されるか、ハード ドライブ上のすべてのファイルが削除される可能性があります。ANSI に関する限り、どちらも正しい動作です (少なくとも、Harbison と Steele はそう言っています)。
そうは言っても、これは本当に奇妙な行動です。printf
次のような単純なことをすると、何が起こっているかがわかります。
printf("%s\n", NULL);
gcc は (エヘム) これを への呼び出しに分解するのに十分スマート
puts
です。最初のprintf
、これ:
printf("test %s\n", NULL);
gcc が代わりに real への呼び出しを発行するほど複雑
printf
です。
(gcc は、コンパイル時に無効な引数に関する警告を発することに注意してくださいprintf
。これは、*printf
フォーマット文字列を解析する機能がずっと前に開発されたためです。)
-save-temps
これは、オプションを指定してコンパイルし、結果の.s
ファイルを調べることで確認できます。
最初の例をコンパイルすると、次のようになりました。
movl $.LC0, %eax
movl $0, %esi
movq %rax, %rdi
movl $0, %eax
call printf ; <-- Actually calls printf!
(コメントは私が追加しました。)
しかし、2番目のものはこのコードを生成しました:
movl $0, %edi ; Stores NULL in the puts argument list
call puts ; Calls puts
奇妙なことは、次の改行を出力しないことです。これがセグメンテーション違反を引き起こすことがわかっているかのように、気にしません。(これは、コンパイル時に警告されました。)