プロが教えるわが家の防犯対策術!

C言語でのfizzbuzz問題の回答を探していたら、以下のようなコードがありました。

#include <stdio.h>
int main(void){
int i;
for(i = 1 ; i <= 100 ; i++){
printf("%d \0Fizz \0FizzBuzz "+(i%5?(i%3?0:4):(i%3?14:10)),i);
}
printf("\n");
return 0;
}

(引用元:http://revilog.com/2010/08/c-fizzbuzz-printf.html

このコードについての質問です。

printf("%d \0Fizz \0FizzBuzz "+(i%5?(i%3?0:4):(i%3?14:10)),i);
で、以下のことは理解しました。

・\0で区切って文字列の終わりを作っておく
・+演算子でポインタ演算を行なっている
・三項演算子でポインタ演算する量を決めている
・+演算子によって指定したポインタから\0までの文字列を出力する
・Buzzを出力する際は、「FizzBuzz」の途中の「Buzz」を出力している

わかっていないところは、

・特殊文字(\0など)や「%d」は何バイトとして計算するのか
・数字以外を出力する際、%dによって桁数がずれ、変なところから出力されてしまうのではないか(でも実際はならない)

です。
以上2点について回答よろしくお願いします。

A 回答 (10件)

>・特殊文字(\0など)や「%d」は何バイトとして計算するのか



特殊文字でもなんでもないのでふつうに数えてください。

>・数字以外を出力する際、%dによって桁数がずれ、変なところから出力されてしまうのではないか(でも実際はならない)

"%d"などの書式を解釈するのはprintf()系の関数です。
Cコンパイラが解釈するわけではありません。

この回答への補足

>特殊文字でもなんでもないのでふつうに数えてください。
普通に数えるとは、「\0」を1バイトとして数え、%dは代入後の数字で考えるということでよろしいでしょうか?


>"%d"などの書式を解釈するのはprintf()系の関数です。
>Cコンパイラが解釈するわけではありません。
すみません。いまいちよくわかりません。詳しく解説していただけないでしょうか。

補足日時:2012/06/27 23:24
    • good
    • 0

"%d" は何バイトですか?

    • good
    • 0

>普通に数えるとは、「\0」を1バイトとして数え、%dは代入後の数字で考えるということでよろしいでしょうか?



"\0"については、そうですが
"%d"の「代入後」ってどういう意味ですか?

例えば
printf("%d", 123);
とした場合、もしかしてprintf()に"123"という文字列が渡っていると思ってませんか?
この場合printf()に引数として渡ってるのは"%d"と123です。

>すみません。いまいちよくわかりません。詳しく解説していただけないでしょうか。

上で説明してるので省略。
もっと詳細を知りたければprintf()関数の実装例などを読んでみてください。
    • good
    • 0
この回答へのお礼

皆さん、たくさんの回答ありがとうございました。
すべての返信を書くのは大変なので、ここでまとめて返信させていただきます。

質問の件ですが、(多分)理解しました。

(1)メモリ上のどこかに"%d \0Fizz \0FizzBuzz "という文字列を作り、その先頭ポインタを
(2)+演算子でずらしてprintf関数に渡し、
(3)printf関数内で、渡されたポインタを起点として%dなどがあったら整形し標準出力に出力

の順序で処理が行われているということでしょうか。

"%d"はprintf関数に渡す前はただの文字列であり、printf関数の中で"%d"を第二引数のものに変えている。
そうすると、%で1バイト、dで1バイトの合計2バイトとしていつも数えられるから、常に+4とか+10としても問題ないというわけですね。納得しました。
ありがとうございます。

お礼日時:2012/06/28 21:24

#include <stdio.h>


int main(void){
printf("%c\n", "%d \0Fizz \0FizzBuzz "[0]);
return 0;
}
とか試せば、n文字目が何かは分かる。


> %dは代入後の数字で考えるということでよろしいでしょうか?
%dはただの文字列です。
putsに渡してみれば理解できますか?

#include <stdio.h>
int main(void){
int i;
char *format;
for(i = 1 ; i <= 100 ; i++){
format = "%d \0Fizz \0FizzBuzz "+(i%5?(i%3?0:4):(i%3?14:10));
puts(format);
//printf(format,i);
//printf("\n");
}
return 0;
}
    • good
    • 0

>printf("%d \0Fizz \0FizzBuzz "+(i%5?(i%3?0:4):(i%3?14:10)),i);



printf()の第1引数には、
"1 \0Fizz \0FizzBuzz "
"2 \0Fizz \0FizzBuzz "
"3 \0Fizz \0FizzBuzz "
...
"98 \0Fizz \0FizzBuzz "
"99 \0Fizz \0FizzBuzz "
"100 \0Fizz \0FizzBuzz "
が、順に渡ります。
で、これらを、「実際にどう出力するか」を、

>+(i%5?(i%3?0:4):(i%3?14:10))

ここでコントロールしています。
    • good
    • 0

先ほどの私の回答は、いささか正確さを欠いていましたね。



>printf("%d \0Fizz \0FizzBuzz "+(i%5?(i%3?0:4):(i%3?14:10)),i);

printf()の第1引数は、あくまで
"%d \0Fizz \0FizzBuzz "+(i%5?(i%3?0:4):(i%3?14:10))
ですね。で、第1引数の一部を構成している
"%d \0Fizz \0FizzBuzz "
ここに、
"1 \0Fizz \0FizzBuzz "
"2 \0Fizz \0FizzBuzz "
"3 \0Fizz \0FizzBuzz "
...
"98 \0Fizz \0FizzBuzz "
"99 \0Fizz \0FizzBuzz "
"100 \0Fizz \0FizzBuzz "
が順に入っていく、ということでありましょう。
    • good
    • 0

A No.6


それも正確ではないですね。

まず、"%d \0Fizz \0FizzBuzz "はこれらの文字列が格納された領域のポインタとして扱われます。
そこに、+(i%5?(i%3?0:4):(i%3?14:10))という式でポインタ操作を行っているので、iの値によってprintfに渡されるポインタの値自体が異なることになります。
printfに渡されたポインタが指す文字列に"%d"が含まれなければ、printfは引数の2番目に渡されたiを無視して処理します。
    • good
    • 0

#5 にしても #6 にしても, 「正確ではない」どころか「間違っている」と断ずるべきだと思います>#7.



特に #5 の「回答」では, 質問者自身が「わかっていない」としている
・数字以外を出力する際、%dによって桁数がずれ、変なところから出力されてしまうのではないか(でも実際はならない)
に対し余計に混乱させてしまうだけです. #6 にしても, あたかも「printf に渡す前に %d を書き換えている」かのような印象を与えてしまいます (これが間違いであることは #1/#3 で既に指摘されている通り).

#1 および #3 の繰り返しになりますが,
printf("%d", 123)
という関数呼び出しにおいて printf に渡される引数は "%d" と 123 の 2つです. そして, 書式文字列にある変換指示 %d に対し「引数として渡されている (はずの) int の値」を出力するのは printf の仕事です. だから,
「数字以外を出力する際、%dによって桁数がずれ、変なところから出力されてしまう」
などということは起こりえません.
    • good
    • 0

>#8さん



>#5 にしても #6 にしても, 「正確ではない」どころか「間違っている」と断ずるべきだと思います>#7.
どうも失礼いたしました。
断じてください。
    • good
    • 0

トリッキーなコードのトリックに、すっかり引っかかってしまったぜ。

フフフ。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!