• ベストアンサー

C言語の超難解なプログラムについて

WikiのIOCCCのページに書いてあるプログラムで以下のようなプログラムがありました。 main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);} このプログラムがどのような過程でどのように動いているのかを詳しく知りたいです。 プログラムに詳しい方よろしくお願いします。 IOCCCのページ http://ja.wikipedia.org/wiki/IOCCC

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

  • ベストアンサー
  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.3

 あまり考えたくないもろもろの抜け道で動いてます(笑)  順番に書き換えましょう。  まず、引数の一つ目の式。  &unix["\021%six\012\0"]  この式において、unixは、1と定義されています。(デフォルトのプリプロセッサマクロです。)  &1["\021%six\012\0"]  となります。  さて、C言語の配列とアドレス演算の表記の問題です。  a[b]という表現は、*(a+b)と等価です。*(a+b)は、*(b+a)と等価です。最後の式を配列表現に戻すと、b[a]となります。ですから、a[b]とb[a]は等価です。  文字列リテラルは、書き換え不可の領域に格納された配列の一表現形式です。文字列リテラルは配列として扱えます。"abcde"[1]は、'b'です。  さて、ですから、問題の式は、次のように書き換え可能です。  &"\021%six\012\0"[1]  添え字が1ですから、二文字目のアドレスを渡していることになりますので、第一引数を人間向きに書き直すと、次のようになります。  "%six\012\0"  ちなみに、\012は、8進数表記ですが、文字コード表を見ると、\nです。  "%six\n\0"  今、元のコードは、次のようになっています。  main() { printf("%six\n\0",(unix)["have"]+"fun"-0x60);}  さて、第2引数に行きましょう。  unixは、1と置き換えますから、  1["have"]+"fun"-0x60  配列の表記を逆にして、  "have"[1]+"fun"-0x60  ここで、第一項は、2文字目の文字そのものですから、  'a'+"fun"-0x60  項を入れ替えて、  "fun"+'a'-0x60  'a'は、小文字のエーの文字コードですから、  "fun"+0x61-0x60  計算して、  "fun"+1  ここまでで、もとのコードは、次のようになります。  main() { printf("%six\n\0","fun"+1);}  printfの第一引数が%sで始まっているので、第二引数は、文字列へのポインタが期待されていることになります。  それに従って、"fun"+1を解釈すると、文字列リテラルは、文字配列へのポインタと等価ですから、ポインタに1を足して、そのポインタを渡しているとすると、"un"を渡していることになります。  main() { printf("%six\n\0","un");}  はい。これで、終着です。  このプログラムの実行結果は?  原記事に書いてあるとおりですね。  だれよ。こんなの考えるの=^・・;=  久しぶりに、Cの解読やったわ。まぁ、お遊び企画の例題だからこれで良いわけですけど。いかにコーティング規約が重要かを再認識されてくれます=^・。・^=

kracfire
質問者

お礼

上から読み進めていったところ納得できました。 こんなにいろいろな捻りを加えて書かれていたのですね。 改めてコーディングについてきちんと考えなおさないといけないという指標になりました。 とても細かく回答していただきありがとうございました。

その他の回答 (2)

  • notnot
  • ベストアンサー率47% (4848/10262)
回答No.2

どの辺でつまづいているのでしょう? #define unix 1 なので、 main() { printf(&1["\021%six\n\0"],(1)["have"]+"fun"-0x60);} 説明にあるとおり、x[y] と y[x] は同じなので、 main() { printf(&("\021%six\n\0"[1]),"have"[1]+"fun"-0x60);} インデクス1の場所の文字とそのアドレスなので、 main() { printf("%six\n\0",'a'+"fun"-0x60);} 'a' は 0x61なので、 main() { printf("%six\n\0","fun"+1);} 無駄な部分を省くと、 main() { printf("%six\n","un");}

kracfire
質問者

お礼

回答ありがとうございます。 つまづいていると言うよりも、C言語の細かい仕様をきちんと把握してないためになんとなくでしか理解できてなかったので詳しい解説が欲しいと思い質問させて頂きました。

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

まさにそのページに書いてあるわけですが, どこが分からないんでしょうか?

kracfire
質問者

補足

確かに書いてあることで納得することはできるのです。 言い換えると、どのようなテクニックでどのように動かしているのかです。 &unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60 の部分が意味不明です。

関連するQ&A

  • 本に載ってた難解なプログラム

    とある本に載っていたもので main(){ printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);} (ヒント:#define unix 1) と、いうプログラムです。 実行してみてもやっぱりわかりませんでした。 解けないとどうなるという状況ではないので 暇なときに考えてやって下さい。

  • C言語のプログラムでうまく動きません。

    xとyを入力してその商を求めよという問題なのですが、大きい数字を小さい数字で割るようになっています。また、当然分母が0の時には不定となるので、次のようなプログラムを作りましたが、うまく動きません。どこが悪いか考えてもわかりませんでした。教えていただけませんでしょうか。 #include<stdio.h> main() { int x; int y; double syou; printf("xを入力せよ"); scanf("%d",&x); printf("yを入力せよ"); scanf("%d",&y); syou=x/y; if("syou>=1") { if("y==0") { printf("不定です"); } printf("答えは%fです。",&syou); } else { if("x==0") { printf("不定です"); } syou=y/x; printf("答えは%fです。",&syou); } } これでx、yを入力しても不定と出てきて、xの方が大きいときには見当はずれの大きな数字が出てきます。逆にyの方が大きいと同じく不定と出てきて答えは0.000と表示します。

  • 「*」←この記号はなに?【C言語】

    http://ja.wikipedia.org/wiki/Gets このページなど、ウィキペディアなどでC言語の関数のページ(getsとかfopenとかなんでも)を見ると、形式の部分で :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::     char *gets(char *s)        :: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ↑このように*が入って説明されているじゃないですか。*はポインタの前につけるんですよね?でもgetsはポインタじゃないです。 ソースコードの例でも、 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::    char a[10];             :: ::    gets(a);              :: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ↑のように*は省かれています。 このような「*」は一体何を意味するんでしょう? 回答よろしくお願いします。

  • C言語のプログラムについて

    三角形を判定するプログラムを作ったのですが直角三角形ができるはずがないのに直角三角形の判定が出てしまいます。簡単なことなのかもしれませんが自分ではわからなくなってしまったのでご指摘してもらいたいです。 #include<stdio.h> void tri(int x,int y,int z) { if((x*x==y*y+z*z)||(y*y==x*x+z*z)||(z*z==x*x+y*y)) { printf("これは直角三角形です。"); printf("これは三角形です。"); }else if((x+y>=z)||(x+z>=y)||(y+z>=x)) { printf("これは三角形ではありません。"); }else{ printf("これは三角形です。"); } } int main(void) { int e1,e2,e3; printf("3辺を入力してください"); scanf("%f,%f,%f",&e1,&e2,&e3); tri(e1,e2,e3); return(0); }

  • C言語で円周率を求めるプログラムを解読しています。

    C言語で円周率を求めるプログラムを解読しています。 ウィキ(http://ja.wikipedia.org/wiki/%E5%86%86%E5%91%A8%E7%8E%87)にも載っていましたが、 π/6=で始まる式を使って、 一項一項を順に求めていき、 最後にすべての項を足したものに6をかけて πを求めるという方法を使っています。 誤差が小さくなるまで繰り返すということで while(a>=1e-10) という表現をしているのですが、 ここのところが理解できません。 どなたか教えてくださいませんか? また、eとは自然対数の底などに用いられるあのeのことでしょうか? C言語初心者なのでお手柔らかに説明していただけると助かります。

  • C言語で困っています。

    正の整数を入れたときに、階乗を求めるプログラムを作りたいのですが、 ↓のプログラムを起動させたら、最終的な答えが1(kaizyo=1)になって、非常に困っています。 何故なのでしょうか?それと、どこを直せばいいですか? #include<stdio.h> main() { int x; int kaizyo=1; printf("整数を入力して下さい:"); scanf("%d",&x); if(x<=0) { printf("値が不適当です。"); } else { while(x<1) { kaizyo=kaizyo*x; x=x-1; } printf("%dの階乗は%dです。",x,kaizyo); } }

  • C言語のプログラムを作りました。

    以下のプログラムは動くかどうか、確認して頂けませんか? おかしい部分があれば指摘して下さい。 また、自分で考えたのですが、このプログラムはどのように考えられたのか、作成方針は考えられますでしょうか? なぜ聞くかというと、皆さんが考えられるように自分も考えているのかどうか、作成方針を聞くことにより判断したいのです。 →作成方針を記述の上、プログラムについてご指摘の方、よろしくお願い致します。 #include<stdio.h> main() { int a,b,c; b=0; c=1; printf("a="); scanf("&d",&a); do{ c=c+1; b=a%c; }while(!(b==0)); if(c==a); printf("sosu-desu n); }else{ printf("sosu-denai n); } }

  • C言語のプログラム

    今授業でCの勉強をしているのですが。以下のプログラムはどうして 計算されないのかがわかりません。型上げされて表示されるのかなと思ったのですが。。まだ初歩の段階ですがよろしくおねがいします。 #include <stdio.h> int main(void) { int vx; double vy; puts("ふたつの数を入力してください"); printf("実数vx"); scanf("%d",&vx); printf("実数vy"); scanf("%lf",&vy); printf("vx+vy=%f\n",vx+vy);/*←vx+vyでdouble型として認識されないのでしょうか?以下同様*/ printf("vx-vy=%f\n",vx-vy); printf("vx*vy=%f\n",vx*vy); printf("vx/vy=%f\n",vx/vy); return(0); }

  • C言語プログラム 配列

    C言語プログラム初心者です。以下のプログラムに対して、実行結果がなぜそうなるかがわからないので、詳しく教えてください。 #include<stdio.h> int func(int a[ ],int b[ ],int n); main(){ int i,k; static int a[12] ={1,2,3,4,5,(省略)・・・,12};  static int b[12] ={21,22,23,24,(省略)・・・,32}; k = func(a,b,6); k = func(b+3,a+3,6); printf("a = "); for(i=0;i<12;i++)printf("%d",a[i]); printf("\n = b"); for(i=0;i<12;i++)printf("%d",b[i]); printf("\n k = %d\n",k); } int func(int a[ ],int b[ ],int n){ int i,x=0; for(i=0;i<n;i++);{ a[i] = b[i]; x = x+a[i]; } return(x); } 結果: a = 21 22 23 .... 26 7 8 9 10 11 12 b = 21 22 23 .... 26 7 8 9 30 31 32 k = 99

  • 1.現代におけるエスペラント語の言語としての位置付

    1.現代におけるエスペラント語の言語としての位置付けとは? 2.エスペラント語、イド語、ノシロ語、リパライン語、ヒュムノス語、シャレイア語などの人口言語の存在意義とは? 言語カテゴリー皆さんの ご回答のほど、 お待ちしております。 https://ja.m.wikipedia.org/wiki/エスペラント https://en.wikipedia.org/wiki/Esperanto http://ja.conlinguistics.wikia.com/wiki/シャレイア語 https://ja.wikipedia.org/wiki/人工言語 https://en.wikipedia.org/wiki/Constructed_language