みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

【C】fizzbuzzのトリッキーなコードについて

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点について回答よろしくお願いします。

投稿日時 - 2012-06-27 22:35:47

QNo.7558882

困ってます

質問者が選んだベストアンサー

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

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

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

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

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

投稿日時 - 2012-06-27 23:51:26

お礼

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

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

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

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

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

投稿日時 - 2012-06-28 21:24:31

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(10)

ANo.10

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

投稿日時 - 2012-06-28 14:21:20

ANo.9

>#8さん

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

投稿日時 - 2012-06-28 14:17:03

ANo.8

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

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

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

投稿日時 - 2012-06-28 12:02:54

ANo.7

A No.6
それも正確ではないですね。

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

投稿日時 - 2012-06-28 09:03:51

ANo.6

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

>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 "
が順に入っていく、ということでありましょう。

投稿日時 - 2012-06-28 08:14:18

ANo.5

>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))

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

投稿日時 - 2012-06-28 07:59:05

ANo.4

#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;
}

投稿日時 - 2012-06-28 02:00:02

ANo.2

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

投稿日時 - 2012-06-27 23:45:06

ANo.1

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

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

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

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

投稿日時 - 2012-06-27 23:01:30

補足

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


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

投稿日時 - 2012-06-27 23:24:15