とある本に載っていたもので

main(){ printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
(ヒント:#define unix 1)

と、いうプログラムです。
実行してみてもやっぱりわかりませんでした。

解けないとどうなるという状況ではないので
暇なときに考えてやって下さい。

このQ&Aに関連する最新のQ&A

A 回答 (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
    • good
    • 0

配列の解釈と文字とコードの変換が頭に浮かぶかどうかが問われているわけですね。



"abc"[1] → *("abc" + 1)
1["abc"] → *(1 + "abc")

このあたりの概念は馴染みずらい所かもしれない。
    • good
    • 0

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");}
ということです。
    • good
    • 0

そもそもどういう問題なのでしょう?


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

この回答への補足

えーと、なんでも

IOCCC(International Obfuscated C Code Competition:不明瞭なCプログラムの国際大会)の1987年に優勝した、ペル研のDavid Kornの作品

だそうで、作者は実行結果を予想できるか聞いていますが
私はなぜそうなるかが知りたいわけでして…

補足日時:2001/11/08 16:25
    • good
    • 0

このQ&Aに関連する人気のQ&A

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

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

QC言語のソースコードの書き方

C言語のソースコードの書き方に関する質問です。
0を5個と1を4個の、合わせて9個の数字を並べて出来上がる数列のパターン(126通り)を全て羅列させるプログラムを作りたいと考えていますが、そのためにどういった方針を立ててソースコードを書けばよいかが分かりません。
どういった構造かだけでも構いませんので、教えて頂けると幸いです。
よろしくお願いします。

Aベストアンサー

という方針で書くとこんな感じね。
#include <stdio.h>

void
print_pattern(char pattern[], int size)
{
for (int i = 0; i < size; i++) {
printf("%c", pattern[i]);
}
printf("\n");
}

void
iter(char pattern[], int end, int n, int m)
{
if (n == 0 && m == 0) {
print_pattern(pattern, end);
return;
}

if (n == 0) {
pattern[end] = 'b';
iter(pattern, end + 1, n, m - 1);
return;
}
if (m == 0) {
pattern[end] = 'a';
iter(pattern, end + 1, n - 1, m);
return;
}

pattern[end] = 'b';
iter(pattern, end + 1, n, m - 1);
pattern[end] = 'a';
iter(pattern, end + 1, n - 1, m);
}

int
main(void)
{
int m = 5, n = 4;
char pattern[m + n];
iter(pattern, 0, m, n);
return 0;
}

という方針で書くとこんな感じね。
#include <stdio.h>

void
print_pattern(char pattern[], int size)
{
for (int i = 0; i < size; i++) {
printf("%c", pattern[i]);
}
printf("\n");
}

void
iter(char pattern[], int end, int n, int m)
{
if (n == 0 && m == 0) {
print_pattern(pattern, end);
return;
}

if (n == 0) {
pattern[end] = 'b';
iter(pattern, end + 1,...続きを読む

Qchar *name1[4] とchar name2[][4] の違いについて

C言語のことで質問があります。

char *name1[4]は
char *name1[4] = {"abcdefghi","jkl","l","mn"};
と宣言でき,ポインタを4つ確保した形となりました。

char name2[][4]は
char name2[][4] = {"abc","def","ghi","jkl","mno","pqr","stu","vwx"};
と4文字以内の文字列を初期化した数だけ確保した形となりました。

この結果からchar *name1[4]の意味は,char name2[][4]ではなくchar name2[4][]に近いと思いました。
しかし,char name2[4][]ではポインタを4つ確保した事にはならないみたいでコンパイルが通りません。
*name1[4]では4つのポインタを確保できるのに~と思ってしまいます。

ポインタと配列は別物と考えるべきなのでしょうか?
訳の分からない質問かもしれませんが,
何卒ご指導いただくようよろしくお願いします。

Aベストアンサー

ポインタと配列の違いというのは、変数と定数の違いのようなものです。

話を簡単にするために、一次元配列から考えましょう。

char *p1; と定義した時のp1は、いうまでもなくポインタで、
これは変数です。p1は任意の文字列を指すことができます。
char a1[4]; と配列の形で定義した場合のa1については、
a1[0]やa1[1]等を、通常のchar型の変数と全く同じように扱うことが
できます。しかし、a1自体は、例えば a1 = p1; のように値を代入する
ことができません。(逆の p1 = a1; は可能。)つまり、この場合のa1は、
変数ではなく、定数のようなものなのです。

複合的なケースについて見てみましょう。
char **q1; ポインタへのポインタ
 q1,*q1,**q1,q1[0],*q1[0],q1[0][0] のいずれも変数として
 扱うことができます。(値を代入することが文法的に許されます。
 ただし、実行時にはアクセス違反になる場合もあります。)
char q2[4][4]; 二次元配列
 q2,q2[0]は変数として扱うことができません。q2[0][0]のように
 して、初めて変数として扱えるようになります。
char *q3[4]; ポインタの配列
 q3は変数として扱うことができませんが、q3[0],*q3[0],q3[0][0]
 はいずれも変数として扱うことができます。
 なお、この定義は char *(q3[4]); とした場合と全く同じ意味です。
char (*q4)[4]; 配列へのポインタ
 q4,(*q4)[0],q4[0][0]はいずれも変数として扱うことができます。
 しかし、*q4,q4[0]は変数として扱うことができません。

char *name1[4]; と char name2[4][]; は確かに似ています。しかし
違うところもあります。それは、name1[0] が変数として扱えるのに
対し、name2[0] には値を代入できないという点です。(データの
具体的な構造については、inthefloiさんが書いておられる通りです。
> char name2[4][]ではポインタを4つ確保した事にはならないみたい
というのも、全くその通りで、配列の定義では、ポインタ変数の領域
を確保する余地はないのです。

ポインタと配列の違いというのは、変数と定数の違いのようなものです。

話を簡単にするために、一次元配列から考えましょう。

char *p1; と定義した時のp1は、いうまでもなくポインタで、
これは変数です。p1は任意の文字列を指すことができます。
char a1[4]; と配列の形で定義した場合のa1については、
a1[0]やa1[1]等を、通常のchar型の変数と全く同じように扱うことが
できます。しかし、a1自体は、例えば a1 = p1; のように値を代入する
ことができません。(逆の p1 = a1; は可能。)つまり...続きを読む

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

以下のソースコードを学習用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言語開発環境で行ったのですが、
『ファイル「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("...続きを読む

Aベストアンサー

>>おかげで、エラー表記されずに、プログラムが実行されました。

これはコンパイルがうまく完了したってことでしょうか?できあがったプログラムが実行できたってことではないですよね?そうなら

>>’タイトル’は内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。」

なんて問題は起きないはすですからね。

できあがったファイルは、XXXX.EXEのように拡張子のEXEがついていますか?もしXXXX.OBJであれば、リンクができていませんから、実行できません。

学習用C言語開発環境の使い方を確認してみてください。

P.S.
昔は、コンパイラを使うのは大変でしたが、今は楽ですね。CではなくPascal系コンパイラーですが、カセットテープに入っていて、コンパイラの読込にテープレコーダで15分かかったりとか、まあ大変だけど面白い時代でした。

Q[VC++][MFC][SDI]ダイアログコントロールもしくわツールバー!!詰まってます!!

現在VC++6.0でMFCを使いSDIのプログラムを作っているのですが、フォームにダイアログバーを張っています。

これをツールバーのように自由に動かしたり、左右上下にドッキングさせたり。
これって実現可能でしょうか?

色々ためしてみたんですが無理でした。
やっぱ不可能ですかね?

これが無理なんであればデフォルトで作成されるツールバー([新規作成][開く][保存]等が入ってる)にコントロール(ボタンやリストボックス)を追加して使用、と考えているのですがこれも難しい!!!


まだまだ未熟者なのですがどうかご存知の方おられましたらご教授ください!!
よろしくお願いします!!

Aベストアンサー

Dannerです。
参考URL(英語)を載せておきます。

参考URL:http://www.codeguru.com/Cpp/controls/toolbar/placingcontrolsintoolbars/article.php/c2505/

QC言語ソースコードに関する質問です。

以下の数列について,初項から第15項までを求めるプログラムと実行結果を示せ。
0 1 1 2 3 5 8 13 21(ただし,初項=0,第1項=1とする。)

ソースコードを書くと、エラーがでた。
#include <stdio.h>
int fib(int n)
{
if(n==1 || n==2)
return 1;
else
return fib(n-1)+fib(n-2);
}
int main(void)
{
int n;
for(n=0;n<17;n++)
printf("%d,",fib(n));
}

正しソースコードを教えてください!

よろしくお願いします。

Aベストアンサー

#include <stdio.h>

void fib(int i, int j, int n)
{
if (n == 1)
{
printf("%d\n", i);
}
else
{
printf("%d\n", i);
return fib(j, i + j, n - 1);
}
}

int main(void)
{
fib(0, 1, 15);

return 0;
}

Qprintfの出力内の文字をdefineしたい

defineで定義した内容をprintfしたいのですが、どうすればいいですか?
次のようなコードを書きましたがコンパイルが通りませんでした。

#define ALP2 B

int main(){

printf("A"ALP2"C");

return 0;

}

上記のコードで「ABC」という出力を得たいです。
できれば変数に代入してから出力というのは避けたいので、それ以外の方法がありましたら教えてください。

Aベストアンサー

#define ALP2 "B"
int main(){
printf("A%sC", ALP2);
return 0;
}
じゃだめなのかな?

Qc言語のソースコードを教えて下さい

キーボードから10個の正整数値を読み込み,合計値を表示するプログラムを作りなさい。
ただし,キーボードから読み込んだ値はint型変数xにしまわれるものとし,変数はこのxと回数を数えるint型変数countと合計値をしまうint型変数sumのみを用いることとする。

このプログラムのソースコードを教えて下さい。
解説もよろしくお願いします。

Aベストアンサー

一例です。
添付のURLを参照して下さい。
因みに、平均値も算出しているが気にせずに、後はご自身で変数、コードを要調整して下さい。

参考URL:http://www.geocities.jp/kenji_y0328/crenshu/renshu/r021.gif

Qp[-3][-4]にアクセス出来るようにしたい

ポインタのポインタをうまく使って、p[-3][-4]のようなアクセス(が有効に行われるように)したいのですが、うまくいきません。
ご教授願えないでしょうか。

以下のコードで p[-3][0] を a[0][0] を参照させる(?)ようにはできました。
後ろ側の添え字のマイナスシフトができないのです。
つまり p[-3][-4] が a[0][0] を参照させるようにするギミックが知りたいのです。よろしくお願いします。

-------------------------------------

int **a;

// 領域の動的確保 ( [12][18] )
a = new int*[12];
for(int i = 0; i < 12; i++)
a[i] = new int[18];


int **p;

p = &(a[3]) ;

for (int y=0; y<12;y++)
for (int x=0; x<18;x++)
a[y][x] = 1;

p[-3][0] = 99; //a[0][0]が99になる。


//領域の解放
for(int i = 0; i < 12; i++)
delete [] a[i];

delete [] a;

ポインタのポインタをうまく使って、p[-3][-4]のようなアクセス(が有効に行われるように)したいのですが、うまくいきません。
ご教授願えないでしょうか。

以下のコードで p[-3][0] を a[0][0] を参照させる(?)ようにはできました。
後ろ側の添え字のマイナスシフトができないのです。
つまり p[-3][-4] が a[0][0] を参照させるようにするギミックが知りたいのです。よろしくお願いします。

-------------------------------------

int **a;

// 領域の動的確保 ( [12][18] )
...続きを読む

Aベストアンサー

どうしてもp[-3][-4]がa[0][0]を参照するようにしたいのでなければ、次のようにしてはどうでしょう。

-------------------------
int **a;

// 領域の動的確保 ( [12][18] )
a = new int*[12];
for(int i = 0; i < 12; i++)
 a[i] = (new int[18]) + 4; // ← 変更点

int **p;

p = &(a[3]) ;

for (int y=0; y<12;y++)
 for (int x=0; x<18;x++)
  a[y][x] = 1;

p[-3][0] = 99; //a[0][0]が99になる。

//領域の解放
for(int i = 0; i < 12; i++)
 delete [] (a[i] - 4); // ← 変更点

delete [] a;
-------------------------

上のコードでは、p[-3][-4]がa[0][-4]を参照することになりますが、目的は満たせるのではないでしょうか。

どうしてもp[-3][-4]がa[0][0]を参照するようにしたいのでなければ、次のようにしてはどうでしょう。

-------------------------
int **a;

// 領域の動的確保 ( [12][18] )
a = new int*[12];
for(int i = 0; i < 12; i++)
 a[i] = (new int[18]) + 4; // ← 変更点

int **p;

p = &(a[3]) ;

for (int y=0; y<12;y++)
 for (int x=0; x<18;x++)
  a[y][x] = 1;

p[-3][0] = 99; //a[0][0]が99になる。

//領域の解放
for(int i = 0; i < 12; i++)
 delete [] (a[i] - 4); // ← 変更...続きを読む

Qソースコードの間違い (C言語)

変数に、文字列を入れた配列の文字列の最後の要素数を入れたいのですが(つまり'\0')、うまくいきません。いつも2個多い値になってしまいます。

#include <stdio.h>

void main() {
char moji[100]={0};
int c=0;

fgets(moji,sizeof moji,stdin);

while( moji[c] != '\0' ) ++c;

printf("\n%d\n",c); //
}

例えば5文字の1ビット文字を入れると、最後の文字はmoji[4]にあるのでprintfで4と表示されるはずじゃないですか。でも6になるんです。いつも+2の値になるんですよ。どうやらfgetsを使っているからそうなるらしく、scanfを使うと結果は1多い値に、普通に配列に直接文字列を代入すると正常な結果になります。別にcに-2してもいいのですが、それはなんだか癪といいますか・・・。なぜこういうことがおきるのでしょうか?回答よろしくお願いします。

Aベストアンサー

>なぜこういうことがおきるのでしょうか
そのmoji[]の余計な部分にはどんなコードが入っているかは確認していますか?
リターンキーも「キー入力」の一つですよ。

Q[OK]/[キャンセル]ボタンがついたダイアログの作り方。

すごく基本的なことですが、[OK]/[キャンセル]ボタンがついたダイアログで、ドキュメントとビューでのデータのやり取りが自信がないので教えてください。

たとえば、顧客データベースのアプリケーションがあって、ある顧客のデータを修正するためのダイアログを考えます。(顧客リストから顧客Aを選択して、編集ボタンを押すと、その修正のためのダイアログがでるとします。)

この手のダイアログにはよく[OK][キャンセル]ボタンがついていますよね。
私が考えた方法は以下のとおりです。

まず、顧客Aのデータ(Documentオブジェクト)としては

(1)データベースの中にある「顧客Aのデータ」(本データ)
(2)ダイアログで編集中の「顧客Aのデータ」(一時データ)

の2つを用意します。そして、

・ダイアログ(Viewオブジェクト)を起動するとき、(1)をコピーして(2)を作ります。
・ダイアログで編集するのは(2)です。
・ダイアログで [OK]を押すと(2)を(1)にコピーします。
・[キャンセル]を押すと、単に(2)を破棄して終わりです。

このように、[OK]/[キャンセル]ボタンがついた編集ダイアログなどでは、2つ同じデータのオブジェクトを作るものなのでしょうか。他に方法が思いつきません。

すごく基本的なことですが、[OK]/[キャンセル]ボタンがついたダイアログで、ドキュメントとビューでのデータのやり取りが自信がないので教えてください。

たとえば、顧客データベースのアプリケーションがあって、ある顧客のデータを修正するためのダイアログを考えます。(顧客リストから顧客Aを選択して、編集ボタンを押すと、その修正のためのダイアログがでるとします。)

この手のダイアログにはよく[OK][キャンセル]ボタンがついていますよね。
私が考えた方法は以下のとおりです。

まず、顧客...続きを読む

Aベストアンサー

もし、
(1)データベースの中にある「顧客Aのデータ」(本データ)
(2)ダイアログで編集中の「顧客Aのデータ」(一時データ)
がどちらもリストであれば、ダイアログを表示する時に、(1)のリストをダイアログに貼り付ける(親ウインドウをダイアログにする)と、(2)が必要なくなります。


人気Q&Aランキング

おすすめ情報