• ベストアンサー

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

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

  • ベストアンサー
  • wormhole
  • ベストアンサー率28% (1622/5659)
回答No.3

>普通に数えるとは、「\0」を1バイトとして数え、%dは代入後の数字で考えるということでよろしいでしょうか? "\0"については、そうですが "%d"の「代入後」ってどういう意味ですか? 例えば printf("%d", 123); とした場合、もしかしてprintf()に"123"という文字列が渡っていると思ってませんか? この場合printf()に引数として渡ってるのは"%d"と123です。 >すみません。いまいちよくわかりません。詳しく解説していただけないでしょうか。 上で説明してるので省略。 もっと詳細を知りたければprintf()関数の実装例などを読んでみてください。

kuro917
質問者

お礼

皆さん、たくさんの回答ありがとうございました。 すべての返信を書くのは大変なので、ここでまとめて返信させていただきます。 質問の件ですが、(多分)理解しました。 (1)メモリ上のどこかに"%d \0Fizz \0FizzBuzz "という文字列を作り、その先頭ポインタを (2)+演算子でずらしてprintf関数に渡し、 (3)printf関数内で、渡されたポインタを起点として%dなどがあったら整形し標準出力に出力 の順序で処理が行われているということでしょうか。 "%d"はprintf関数に渡す前はただの文字列であり、printf関数の中で"%d"を第二引数のものに変えている。 そうすると、%で1バイト、dで1バイトの合計2バイトとしていつも数えられるから、常に+4とか+10としても問題ないというわけですね。納得しました。 ありがとうございます。

その他の回答 (9)

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.10

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

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.9

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

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.8

#5 にしても #6 にしても, 「正確ではない」どころか「間違っている」と断ずるべきだと思います>#7. 特に #5 の「回答」では, 質問者自身が「わかっていない」としている ・数字以外を出力する際、%dによって桁数がずれ、変なところから出力されてしまうのではないか(でも実際はならない) に対し余計に混乱させてしまうだけです. #6 にしても, あたかも「printf に渡す前に %d を書き換えている」かのような印象を与えてしまいます (これが間違いであることは #1/#3 で既に指摘されている通り). #1 および #3 の繰り返しになりますが, printf("%d", 123) という関数呼び出しにおいて printf に渡される引数は "%d" と 123 の 2つです. そして, 書式文字列にある変換指示 %d に対し「引数として渡されている (はずの) int の値」を出力するのは printf の仕事です. だから, 「数字以外を出力する際、%dによって桁数がずれ、変なところから出力されてしまう」 などということは起こりえません.

回答No.7

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

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.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 " が順に入っていく、ということでありましょう。

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.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)) ここでコントロールしています。

  • Gotthold
  • ベストアンサー率47% (396/832)
回答No.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; }

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

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

  • wormhole
  • ベストアンサー率28% (1622/5659)
回答No.1

>・特殊文字(\0など)や「%d」は何バイトとして計算するのか 特殊文字でもなんでもないのでふつうに数えてください。 >・数字以外を出力する際、%dによって桁数がずれ、変なところから出力されてしまうのではないか(でも実際はならない) "%d"などの書式を解釈するのはprintf()系の関数です。 Cコンパイラが解釈するわけではありません。

kuro917
質問者

補足

>特殊文字でもなんでもないのでふつうに数えてください。 普通に数えるとは、「\0」を1バイトとして数え、%dは代入後の数字で考えるということでよろしいでしょうか? >"%d"などの書式を解釈するのはprintf()系の関数です。 >Cコンパイラが解釈するわけではありません。 すみません。いまいちよくわかりません。詳しく解説していただけないでしょうか。

関連するQ&A

  • C言語のポインタについて教えてください。

    C言語のポインタについて教えてください。 ・pointer1.c  int main(){   int a;   int *p;   p = &a;     a = 123;   printf("%d", *p);   return 0;  } ・pointer2.c   int main(){ int a[100]; int *p; p = &a[0]; int i; for(i = 0; i < 100; i++) a[i] = i; for(i = 0; i < 100; i++) printf("%d", *p++); return 0; } と二つのソースコードがあるとき、pointer2.cの「p = &a[0]」をpointer1.cのように「p = &a」と書けないのはなぜですか?  また、「&a」は動かすことのできなく、「aを指し示す*p」は動かすことができる変数のようなもの、という認識に誤りはないでしょうか?  宜しくお願いします。

  • C言語:配列の中の文字を。。。

    こんにちは。 閲覧していただきありがとうございます。 長文になりますが勉強がわからず困っているので、ご助力いただけると助かります。 C言語についてです。 文字列の中に、特定の文字が出てきたときにだけ改行して表示するプログラムを作りたくて、以下のようにしてみました。(文字はアルファベット小文字のみです) #include<stdio.h> #include<string.h> int main(){  char str[200];  int i;  int k;  puts("文字列を入力");  gets(str);  k = strlen(str);  for(i=0;i<k;i++){    if(strcmp(str[i],"n")==0)     printf("\n");    printf("%c",str[i]);    }   return 0; } このプログラムにより期待する結果は、例えば入力した文字列が「abcdne abcn d」だとすれば、nが来たときに改行が実行されて abcd ne abc n d と表示されるようにしたいのですが、strcmpにintは渡せないとか、ポインタを使ってみても、実力不足ゆえに、「移植性のないポインタ変換」とかのエラーしか出ません。 どのようにすれば、この結果を得ることができるのでしょうか? ぜひご教授ください。よろしくお願いします。

  • C言語で分からないところがあるのですが

    すみません。C言語のポインタで分からないことがあって来ました。 ポインタの理屈は理解してはいるのですが、いざソースコードを書いてみようということになると全く手がつけられずにいます。 以下のソースコードですが、strlen()と同じ働きをする関数mystrlen()と、strcmp()と同じ働きをする関数mystrcmpを、ポインタを使って作成するものです。どこをどうすればいいのか教えてくださいませんか。 #include <stdio.h> int main(void) { char str1[80], str2[80]; int i, j; int len1, len2; printf("第1の文字列を入力してください: "); gets(str1); printf("第2の文字列を入力してください: "); gets(str2); /* * 文字列の長さを確認する */ /* NULL文字(文字列の最後)まで読み飛ばす */ for (len1 = 0; len1 < 80 && str1[len1] != '\0'; len1++) ; /* ループ終了後、len1 に文字列の長さが入っている */ if (len1 < 80) { printf("%s は %d 文字の長さです\n", str1, len1); } else { printf ("第1の文字列が80字以上あります\n"); } /* str2 についても同様 */ for (len2 = 0; len2 < 80 && str2[len2] != '\0'; len2++) ; if (len2 < 80) { printf("%s は %d 文字の長さです\n", str2, len2); } else { printf ("第2の文字列が80字以上あります\n"); } if (len1 < 80 && len2 < 80) { for (i = 0; i < 80 && str1[i] != '\0' && str2[i] != '\0' && str1[i] == str2[i]; i++) ; if (str1[i] == str2[i]) { /* 両者同時に == '\0' のはず*/ printf("文字列は等しい\n"); } else if (str1[i] < str2[i]) { /* str1[i] == '\0' のはず*/ printf("%s は %s より小さい\n", str1, str2); } else { /* str2[i] == '\0' のはず*/ printf("%s は %s より大きい\n", str1, str2); } } /* * 十分なスペースがあれば、str2をstr1の最後に連結する */ if (len1 + len2 < 80) { /* str1 の末尾を探す */ for (i = 0; str1[i] != '\0'; i++) ; /* ループを抜けた段階では i は len1 と同じはずなので、 上記のループを作らず、i の代わりに len1 を用いるのも可 */ /* それ以降に str2 の中身をコピーする */ for (j = 0; str2[j] != '\0'; j++) { str1[i+j] = str2[j]; } str1[i+j] = '\0'; printf("%s\n", str1); } else { printf ("文字列をつなげた長さが80字以上あります\n"); } /* * str2をstr1にコピーする */ if (len1 + len2 < 80) { for (i = 0; str1[i] != '\0'; i++) { str1[i] = str2[i]; } str1[i] = '\0'; printf("%s %s\n", str1, str2); } return 0; }

  • C言語のソースコードについて教えてください。

    以下のソースコードを学習用C言語開発環境で行ったのですが、 『ファイル「C:/Users/ユーザー名/AppData/Local/EasyIDEC/project/タイトル/main.c」の 「41行目」で記述エラーを発見しました。 「,」を付け忘れています。』 という、コンパイルエラーが表示されました。 何度も見直したのですが、よくわかりません。 #include <stdio.h> int main(int argc, char *argv[]) { char answer ; answer = 'n' ; while(answer =='n') { int input ; input = 0 ; int add ; add = 1 ; int sum ; sum = 0 ; printf("数値を入力して下さい。:") ; scanf("%d", &input ) ; int i ; i = 0 ; while(i < input) { sum =sum + add ; printf("\n%d",sum) ; i++ ; add++ ; } printf("\n1から%dまでの総和は、%dです。" , input , sum) ; while(1) { printf("\n終了しますか? y/n:") ; scanf(" %c , &answer) ; if( (answer != 'y') && (answer != 'n') ) { printf( "y or nを入れてください。") ; } else { break ; } } } return 0 ; }

  • Cプログラミングについて

    XP環境でVisual C++2008を使ってC言語の プログラミングをしています。 キーボードから複数回読み込み、長い文字列を作るというものです。 例) >>文字列を入力 >>abc >>今まで入力された文字列を表示 >>gsfgsdfgjfsdabc 配列、ポインタ、メモリの確保を行う事がヒントとして与えられています。 これを実現するために以下のようなプログラムを組んでみました。 int InputCount=0; char InputStr[10]; char *OutputStr[5]; int i=0; while(InputCount<5){ printf("文字列を入力してください\n"); scanf("%s",InputStr); OutputStr[InputCount]=(char*)malloc(strlen(InputStr)+1); OutputStr[InputCount]=InputStr; printf("今までに入力された文字を表示します\n"); for(i=0;i<=InputCount;i++){ printf("%s",OutputString[i]); } printf("\n"); InputCount++; } このように組んでみたのですが、うまくいきませんでした。 これでは一度入力した文字列の内容を 保持する事ができないようです。 根本的にアルゴリズムが悪いのではないかとも考えていますが、どうするのが良いのかわかりません。 初心者なので至らない点、情報不足があればご指摘願います。 これではなぜ無理なのか、またどのようなアプローチが良いのかご教授よろしくお願いします。

  • C言語で困っています

    C言語で 100:1 99:2 98:3 .......ループ 1:100とやりたいんですが このやりかたがわかりません goto文などを使うのでしょうか? 一応コードは #include <stdio.h> int main(void) { int i,a; for(i=100; i>=1; i--){ for(a=1; a<=100; a++){ printf("%d:\n",i); break; printf("%d",a); } } return 0; } です まったくこれだと右側が出力されなくてだめみたいです どなたかお知恵をください お願いします

  • C++

    文字を入力して、それを1つ右にずらして出力する問題。 例  aret → tare 関数内でポインタを使用するのが条件です。 ズラしているつもりが、入力した文字がそのまま出力されてしまいます。 自分の方針としては、 関数内で入力された文字の長さを確認。 文字列の最後の文字をtmpに保存して ドンドン右にずらして保存していく。 最後にtmpに保存したものを、文字列の最初に入れる。 というものです。 わかるかた、ご指導お願いします。 #include<stdio.h> #include<string.h> void kaiten(char *t) { int len =0; int i; char tmp; while (*t++){ len++; } tmp = *(t+(len-1)); for(i=len-1;i>0;i--) *(t+i)=*(t+i-1); *t = tmp; } int main() { char s[100]; printf("文字列を入力:"); scanf("%s",s); kaiten(s); printf("一文字ずらすと:%s\n",s); }

  • C++言語で文字列を出力するにはどうしたら良いですか。

    C++言語で文字列を出力するにはどうしたら良いですか。 今、ポインタの勉強中ですが、ポインタの使い方がよく分からないです。 次のコードで ---------- one two three ---------- を出力したいのですが、nしか出力できません。 どうやらoneのnしか出力できません。 どのようなコードを書いたら解決できますでしょうか。 ------------------------------------------- #include<iostream> using namespace std; char name[3][10]={{"one"},{"two"},{"three"}}; char f(const char *name,int i){ return name[i]; } int main(){ cout << f(name[0],1) << endl; } -------------------------------------------

  • C言語の問題

    以下はC言語の問題です。お教えください。 1000以下の素数を求めるプログラム prog.c を作成せよ。各素数を整数4桁で出力し、15個の素数を出力した時点で改行処理 を行うこと。作成したプログラムを提出せよ。 です。 僕の考えでは、 #include <stdio.h> #include <math.h> main(){ int i; int j; int ix; int k; printf("正の整数を入力して下さい: "); scanf("%d",&i); ix=(int)(sqrt((double)i)); k=0; for(j=2;j<=ix;j++) { if(i%j==0) { k=1; } } if(k==0) { printf("%d は素数です\n",i); } else { printf("%d は素数ではありません\n",i); } となると思うのですが。どうやら違うようです。全然わからないので、正しい答えを教えてください。

  • C言語の質問です><

    C言語の質問です>< 次のような実行結果が得られるプログラミングをしたいのですが・・ ちなみにポインタや標準関数のstrシリーズは使用不可です。 文字列1:ABCDEFGHIJ 開始位置:0 文字数:3 文字列2:ABC 開始位置が7で文字数が5とかの場合は'¥0'の位置まで表示するようにしたいのですが、自分の以下のプログラムだと開始位置が0で文字が3だとABCと表示できるのですが、開始位置が7で文字数が5とかだとできません>< #include<stdio.h> void main(void) { char m1[]="ABCDEFGHIL"; char m2[11]; int i,j,start,mozikazu; printf("文字列1:%s\n",m1); printf("開始位置:"); scanf("%d",&start); if(start >=0 && start <11) { printf("文字数:"); scanf("%d",&mozikazu); } if((start+mozikazu)<11) { for(i=0;i<mozikazu;i++) { m2[i]=m1[i]+start; } m2[i]='\0'; } printf("文字列2:%s\n",m2); return; } どうか教えてください><