• ベストアンサー

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

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

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

  • ベストアンサー
  • Jizou
  • ベストアンサー率80% (4/5)
回答No.4

Jizouと申します。 順に考えていきます。 main(){ printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);} (ヒント:#define unix 1) == Step 1: ========================================================= #defineマクロはソースコードのコンパイルが行われる前に実行されます から、ソースコード内の unix という文字列はすべて 1 という文字に 置き換えられます。置き換えたものが次のソースです。 main(){ printf(&1["\021%six\012\0"],(1)["have"]+"fun"-0x60);} == Step 2: ========================================================= 次に "\021%six\012\0" を解釈します。 文字列中の \0nn という表現は nn を8進数として解釈しますので、\021 は 10進数で 17( = 8 * 2 + 1)、16進数で 11 を示す値となります。同じよ うに \012 は 10進数で 10( = 8 * 1 + 2 )、16進数で 0a を示す値とな ります。\0 は 0(ゼロ)です。   \021 == 17 == \0x11   \012 == 10 == \0x0a ASCIIコード表で 17 が何を表すか分かりませんが、10 は「改行」を表しま すので '\n' と同じ意味です。(改行と '\n' が厳密に同じかどうかはちょ っと参考書をひっくり返さなければ分かりませんが、今回のケースでは同じ と考えても問題ないと思います) 文字列の最後はもともと終端コードとして '\0' が1個入っていますので、 このソースの場合には '\0' コードが2個入ることになります。 結局 "\021%six\012\0" は "\0x11%six\n" と同じ意味になります。 main(){ printf(&1["\0x11%six\n"],(1)["have"]+"fun"-0x60);} == Step 3: ========================================================= 次が最も難解な部分だと思いますが、&1["\0x11%six\n"] を解釈します。 これは良く使う形としては answer_text[3] という形と同じです。 C言語では文字列変数を char answer_text[100]; のように定義すると answer_text というラベルは宣言した文字列の先頭アドレスを表すラベルと して扱われます。 この文字列の4文字目を取り出す場合に answer_text[3] という書き方をし ますが、これは内部的には次のような形に変換される規則になっています。   answer_text[3] ===> *( answer_text + 3 ) answer_text というアドレスに 3 を加えて、そのアドレスから1文字取り 出すというわけです。 ところがこの変換式は answer_text の位置と 3 の位置に何を書いても上記 のように変換される規則になっています。ですので answer_text と 3 の位 置を入れ替えてもエラーなくコンパイルできてしまい、意味も同じになりま す。   3[answer_text] ===> *( 3 + answer_text ) ですので 1["\0x11%six\n"] はカギ括弧の中と外を入れ替えた "\0x11%six\n"[1] と同じ物なのです。この「文字列の後ろにインデックスを添える」という形 も普通は使いませんが、文字列変数も文字列も同じようなものだと思えばこ れは「その文字列の2文字目を示す」ものだということがお分かりいただけ ると思います。   1["\0x11%six\n"] ===> 文字列 "\0x11%six\n" の2文字目を示す。 今回のケースでは先頭にアンパサント(&)が付いていますので、さらに「そ のアドレスを示す」ということになります。つまり   &1["\0x11%six\n"]    ===> 文字列 "\0x11%six\n" の2文字目の先頭アドレスを示す。    ===> "%six\n" ということになります。(要するに先頭の1文字 '\0x11' を読み飛ばす) main(){ printf( "%six\n", (1)["have"]+"fun"-0x60);} == Step 4: ========================================================= お次は (1)["have"] です。 ここで (1) は 1 と同じですので実質的には 1["have"] と同じです。 これは上でやった内容と同じですので次のようになります。   (1)["have"] ===> 1["have"] ===> "have"[1] これは「"have" の2文字目」という意味になりますので、'a' ということ になります。 main(){ printf( "%six\n", 'a'+"fun"-0x60);} == Step 5: ========================================================= その次は 'a'+"fun"-0x60 です。 文字 'a' は16進数では 61 ですので C言語の表記では \0x61 となります。 さてその次の "fun" ですが、ソースコード中の文字列は式の中では「その文 字列の先頭アドレス」を示す値となります。 したがってこの式は次のように解釈されます。   'a'+"fun"-0x60 ===> 0x61 + "fun" - 0x61 ===> "fun" + 1 文字列アドレスに1を加えるということは、2文字目のアドレスを示すとい うことになります。   "fun" + 1 ===> "un" したがって最初のソースコードは次のソースコードと同じ意味になります。 main(){ printf( "%six\n", "un" );} == Step 6: ========================================================= ここで printf関数 の第1引数の中の "%s" は第2引数の文字列で置き換え て出力されますので、最終的には次の文字列が表示されることになるわけで す。   "unix"(+改行) 以上、お分かりいただけたでしょうか? まだ分からない点などありましたら、コメントに書き込んで下さい。 Jizou

その他の回答 (3)

noname#30727
noname#30727
回答No.3

配列の解釈と文字とコードの変換が頭に浮かぶかどうかが問われているわけですね。 "abc"[1] → *("abc" + 1) 1["abc"] → *(1 + "abc") このあたりの概念は馴染みずらい所かもしれない。

  • ranx
  • ベストアンサー率24% (357/1463)
回答No.2

unixは1を表すんですね。知りませんでした。 printf()が一つ呼ばれているだけですから、その引数の意味を示せばよいのだと思いますが、 まず、unix["\021%six\012\0"]ですが、アドレス1を先頭として、文字列"\021%six\012\0"の アドレス分だけプラスした文字のアドレス・・・ということは、とりも直さず 文字列"\021%six\012\0"の2文字目のアドレスということになります。(先頭はゼロですから。) で、\012は\nのことですから、この第一引数は"%six\n"と同じことになります。 第二引数は、同じ理屈で"have"の二文字目ですが、こちらはアドレスではありませんので、 'a'という値、つまり0x61になります。0x61に"fun"のアドレスを加え、0x61を引くということは つまり"fun"の2文字目のアドレスと同じことですから、第二引数は"un"です。 つまり、このプログラムは main(){printf"%six\n","un");} ということです。

noname#6581
noname#6581
回答No.1

そもそもどういう問題なのでしょう? 実行結果がどうなるかという問題ですか? それなら結果は unix と表示されますが。

minimum
質問者

補足

えーと、なんでも IOCCC(International Obfuscated C Code Competition:不明瞭なCプログラムの国際大会)の1987年に優勝した、ペル研のDavid Kornの作品 だそうで、作者は実行結果を予想できるか聞いていますが 私はなぜそうなるかが知りたいわけでして…

関連するQ&A

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

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

  • 数式を読みこんで答えをかえすプログラム

    C言語で、「キーボードから入力された数式(加法、減法限定)の答えをかえすプログラム」をつくりたいのですが。。。 ※4+35+287-43など #include<stdio.h> #include<string.h> #define NUM 40 int main(void){ char str[NUM]; int i, result, x; printf("Please input an expression:\n"); scanf("%s", str); x = strlen(str); result = 0; for(i=0; i<=x; i++){ if((str[i] == '+') || (str[i] == '-')){ // printf(","); } printf("%c", str[i]); } } printfで+と-を「,」で区切って、それをヒントにそれぞれ配列の中に入れるのかな、というところまでは出来たのですが。。。 解り易い解説をつけて下さるとありがたいです。よろしくお願いします。

  • プログラムの動きがわからない

    現在C言語の勉強をしているのですが、プログラムの動きが(流れ)よくわかりません。 #include <stdio.h> int is_digit(char x) { if(((x)>='0')&&((x)<='9')){ return 1; } else{ return 0; } } main() { char c; c='0'; if(is_digit(c)){ printf("cは数値文字です。\n"); } return 0; } 上記のプログラムなのですがどのように値渡しが行われているかがわかりません。 ちなみにこのプログラムを書き直すとこのように↓なるようです このプログラムだけエラーが出てしまいました。 #include <stdio.h> #define IS_DIGIT(X) (((X)>='0')&&((X)<='9'))?1:0) main() { char c; c='0'; if(IS_DIGIT(c)){ printf("cは文字列です。\n"); } return 0; } ちなみに教本はこれを使っています。http://7andy.yahoo.co.jp/books/detail?accd=31457604

  • Visual C++2008のプログラムを変更しても反映されません

    Visual C++2008のプログラムを変更しても反映されません 手元にあるアプリケーションプログラムをデバックしてコンパイル⇒実行⇒動作は正常にできるのですが、 追加されているソース(mainなど)をいじっても変更が反映されません。 (↑main.cにprintfをいれたりコメントアウトしたりなどの簡単な操作) プログラムの知識が乏しいのでうまく説明できなくてすいません。 少しでもヒントにしたいので考えられる原因を教えてください。

  • 16進ダンプのプログラム

    変数の中身を16進数で1バイトずつダンプするにはどんなプログラムを作ったらいいのでしょうか? ためしに、こんなプログラムを作ってみました。 #include <stdio.h> int main() { int intdata=10; char *intptr; intptr=&intdata; printf("%02x %02x %02x %02x\n", *intptr,*(iniptr+1),*(intptr+2),*(intptr+3)); return 0; }  ところが、これでは実行結果が、  0a 00 00 00  となって、思い通りに出力されていないように思います。これは何かプログラムに問題があるのでしょうか?それともこの実行結果でよいのでしょうか?  僕は、 00 00 00 0aと表示されるかと思っていたのですが・・・。

  • RPGゲームの簡単なプログラムを打ってみたんですがうまく表示されません

    RPGゲームの簡単な初歩的なサンプルプログラムを自分で打ってみたんですが、思った通りに表示されません。 以下のソースのどこかおかしいとこありますでしょうか。 #include<stdio.h> #include<windows.h> #define MAP_SIZE_Y 10 #define MAP_SIZE_X 10 int x = 4,y = 5; int j,i; int map[100][100] = { {1,1,1,1,1,1,1}, {1,0,0,3,0,0,1}, {1,0,0,0,0,0,1}, {1,0,2,0,2,0,1}, {1,0,0,0,0,0,1}, {1,1,1,1,1,1,1}, }; void DrawMap(){ for(j = 0; i < MAP_SIZE_Y; j++){ for(i = 0; i < MAP_SIZE_X; i++){ if(j == y && i == x){ printf("勇"); }else{ switch ( map[j][i]){ case 1: printf("■"); break; case 2: printf("兵"); break; case 3: printf("王"); break; default:printf(" "); break; } } } printf("\n"); } } void main(){ while(1) { system("cls"); DrawMap(); } }

  • sinのプログラム

    #include<stdio.h> #include<math.h> #define NMAX 100 main(){ float eps,x,t,s; int n; printf("Taylor series\n"); scanf("%g",&eps); printf("eps=%g\n",&x); for(;scanf("%g",&x)!=EOF;){ printf("\nx=%g\n n\tt\t\ts\n",x); t=s=1; for(n=1;n<NMAX;n++){ t*=X/n; s+=t; printf("%2d %15.6e %15.6e\n",n,t,s); if(fabs(t)<eps) break; } if(n>=NMAX) printf("---not converged ---\n); printf("exp(%g)=%g\tn=%d\n",x,s,n); } return(0); } これはeの級数展開をもとめるプログラムなのでが、これをsinの級数展開のプログラムに改造しろという問題があります。 sinのn乗の項を求めてeの部分と置き換えてやってみたのですができません。 どなたかわかる方がいましたら、教えてください。

  • プログラムの練習問題をやっていたのですが、練習問題の答えのような実行結

    プログラムの練習問題をやっていたのですが、練習問題の答えのような実行結果にならないので教えていただけませんか? 下記に記したプログラムを実行すると x=9 y=-9 [ 9]*[-9]=[ -81],[ 9]*[-8]=[ -72],[ 9]*[-7]=[ -63],・・・・ ・・・・ [10]*[-9]=[ -90],[10]*[-8]=[-80],・・・・ ・・・・ のようになるのですが、 x=9 y=-9 [ 9]*[-9]=[ -81],[10]*[-9]=[-90],・・・ [ 9]*[-8]=[ -72],[10]*[-8]=[-80],・・・ [ 9]*[-7]=[ -63],・・・ ・・・・ のようにするためにはどうすればいいですか? #include <stdio.h> int main(void) { int x,y,m,n; printf("x="); scanf("%d",&x); printf("y="); scanf("%d",&y); for(m=x;m<=x+3;m++) { printf("\n"); for(n=y;n<=y+14;n++) { printf("[%2d]*[%2d]=[%4d],",m,n,m*n); } printf("\n"); } return(0); }

  • プログラムについてです。

    クラスの身体計測簿をつくるプログラムを作成したいのですが、 以下のプログラムの修正点を指摘願います。 項目は、氏名 身長 体重です。 #include <stdio.h> #define MAX_氏名 50 /*maximum length of name */ #define MAX_身長 200 /*maximum length of height code */ #define MAX_体重 120 /*maximum length of weight */ struct person{ char 氏名[MAX_氏名+1]; char 身長[MAX_身長+1]; char 体重[MAX_体重+1]; }; main() { struct person Book; printf("氏名を入力してください.\n"); gets(Book.氏名); printf("身長を入力してください.\n"); gets(Book.身長); printf("体重を入力してください.\n"); gets(Book.体重); puts(Book.氏名); puts(Book.身長); puts(Book.体重); } こんなんでよろしいのでしょうか?? あと、身長順に並び変えたりするにはどのようなプログラムを用いたらよいのでしょうか??

  • プログラムについて質問です。

    プログラムについて質問です。 下記に記載したプログラムは「ふーん」という回答を繰り返す人工無能です。 そのプログラムを if (keyword=="こんにちは") then say("こんにちは 元気ですか?") や if (keyword=="さようなら") then say("バイバイ") と言うように決められたキーワードをに対して決められた返答するにはどのように改良を加えればよいでしょうか? #include<stdio.h> #define MAXLINE 256 int main() { char line[MAXLINE] ;//入力バッファ /*オープニングメッセージ*/ printf("taka:メッセージをどうぞ\n"); printf("自分:"); /*会話しましょう*/ while(fgets(line,MAXLINE,stdin)!=NULL){ printf("taka:ふ~ん?\n"); printf("自分:"); } /*エンディングメッセージ*/ printf("taka:ばいば~い\n"); return 0 ; } どうかよろしくお願いします できればソースを書いていただけると幸いです。

専門家に質問してみよう