2009年07月17日
GCCのprintfビルトイン関数に関しての続き
前回のprintfの話で以下のコメントをいただきました。
> 以下が気になりました。 > -libcに改行なしのputsが無い > -gccが二つ以上のprintfやputs、putcharの合成をしない > 何か理由があるのかな
直接この疑問に答えられているわけではありませんが、ちょっとだけ調べてみたのでここに記しておきます。
libcに改行なしのputsが無い
puts では文字列をstdoutに書き出した後に改行を入れてくれます。便利ではあるのですが、ちょっと余計なお世話?
putsに似た関数でfputs だと文字列だけで改行は入りません。
gccのソースコードでビルトイン関数のprintfの変換をしているところで、興味深いコメントを見つけました。
gcc 4.3.3のgcc/builtins.cの5332行目付近
/* If the format specifier was "string\n", call puts("string"). */
size_t len = strlen (fmt_str);
if ((unsigned char)fmt_str[len - 1] == target_newline)
{
/* Create a NUL-terminated string that's one char shorter
than the original, stripping off the trailing '\n'. */
char *newstr = alloca (len);
memcpy (newstr, fmt_str, len - 1);
newstr[len - 1] = 0;
arg = build_string_literal (len, newstr);
if (fn_puts)
fn = build_call_expr (fn_puts, 1, arg);
}
else
/* We'd like to arrange to call fputs(string,stdout) here,
but we need stdout and don't have a way to get it yet. */
return NULL_RTX;
printfの引数が一つだけの時にputsへの変換ができるかどうか調べているところです。
文字列の最後の文字が改行かどうかのif文のelse節のところで
/* We'd like to arrange to call fputs(string,stdout) here,
but we need stdout and don't have a way to get it yet. */
「ここでfputs(string, stdout)に変換したいけれど、変数stdoutが必要だが今はそれを得る方法がない。」
仮にここでコンパイラが内部的に変数stdoutをひねりだしたとしても、かなり前にもどって処理をやり直さなければならないのでしょう。
gccが二つ以上のprintfやputs、putcharの合成をしない
コンパイルの段階でこれらの関数の変換をするためには引数が定数文字列になっていなければなりません。これは頻度が低そうです。
他のコンパイラではそのような合成をするのでしょうか?
関数の合成ではありませんが、定数文字列は並べて書くと連結してくれます。なので、以下のプログラムはdefineの値によって表示される文字列が異なりますが、コンパイルの段階でひとつの文字列になっています。(つまり実行時に連結されるわけではない。)
#ifdef JAPAN
#define WORLD "Japan"
#else
#define WORLD "World"
#endif
#include <stdio.h>
main()
{
printf("Hello, " WORLD "!!\n" );
}
define値を変えて2通りでコンパイルし、そのアセンブラ出力を比較してみます。
> gcc -o h2.s.world -S h2.c
> gcc -o h2.s.japan -DJAPAN -S h2.c
> diff -u h2.s.world h2.s.japan
--- h2.s.world 2009-07-16 16:10:36.718750000 +0900
+++ h2.s.japan 2009-07-16 16:11:01.312500000 +0900
@@ -12,7 +12,7 @@
.section .rodata
.align 2
.LC0:
- .ascii "Hello, World!!\000"
+ .ascii "Hello, Japan!!\000"
.text
.align 2
.global main
この文字列が(printfから変換された)一回のputsでstdoutに出力されます。