2017-06-11 11 views
3

私はStackOverflowで週末を失い、Hot Network Questionsthis challengeを見ました。CodeGolfからこのコードを理解できません

背景

こんにちはゴルファー!私はすべてのプログラミング言語を学びたいと思います! しかし、私はちょっとした注意深さを持っています...そして、すべてのHelloをコピーしてください 世界の例は退屈です...しかし、私は火が好きです!^^

チャレンジ

ワットだからここはプランです! がコンパイルし、Goodbye Cruel World!を印刷し、クラッシュする最小のコードを書くことを皆さんに願っています。または、 ボーナスツイストチャレンジとして、Hello World! Goodbye Cruelでクラッシュする World!完全にC言語を理解するために喜んで学生として

この挑戦にC answer時にひっかかったとき、私は非常に混乱していた:

main(){puts(puts("Goodbye Cruel World!"));} 

は、文字列を出力し、リターンを使用しようとします値をポインタ として別の文字列に出力すると、セグメント化エラーが発生します。 puts()documentation

おかげで私はputs()は成功時に非負の値を返すことを発見しました。私が正しく理解のであれば、このようなものと同等です:

puts(2); 

2は「を印刷しようとする別の文字列へのポインタ」どのように?

その後、改善はこの非常に同じ答えに追加されました:

main(i){i=puts("Goodbye Cruel World!")/0;} 

そして私は、この時間は完全に失われました。したがって、iはmainからの引数として取られ、戻り値の格納にはputs()が使用されます。 OK。しかし、\0はどうですか?なぜそこにNUL-TERMINATOR文字を使用するのですか?

私を少し明るくしていただけたら、これを理解するのはとても面白いでしょう。また、言い換えれば質問のタイトルはちょっと正確かもしれないと思いますが、私は誤解を言葉に入れられませんでした。

+0

@Downvoter a hint? – Badda

+0

これは有効なCコードではないので、コンパイラはさまざまな理由で文句を言うべきです。そうでない場合は、新しいものを入手するか、標準Cに変更し、推奨される警告を有効にします。そしてわれわれは難読化されたコード==悪いコードの説明サービスではない。 – Olaf

+5

'/ 0'は「0で割る」を意味します。 –

答えて

2

両方の解決策では、未定義の動作が発生します。

最初の溶液:

main(){puts(puts("Goodbye Cruel World!"));} 

puts("Goodbye Cruel World!")を評価し、成功した場合に、負でない値を返します。この値はputs()に渡されます。さて、§6.5.2.2 7によると:

呼び出される関数を表し、式は プロトタイプが含まれないことタイプを持っている場合、引数は暗黙的に対応するの種類に、割り当てであれば として、を変換され、各パラメータの型は、 の 宣言型の非修飾バージョンになります。

ように、コードは、タイプchar *の値に、割り当てたかのように、puts()への最初の呼び出しから返された値を変換しよう。これは代入の左オペランドの型ですが、右オペランドの型はintです。左のオペランドはポインタ型なので、右のオペランドは、互換型の修飾または非修飾バージョンへのポインタ、voidへのポインタ、またはNULLポインタ定数(§6.5.16.1 1)でなければなりません。これらのどれも当てはまりませんので、これは制約違反であり、コンパイラは警告を出す必要があります。

これは未定義の動作です。つまり、このコードを実行した場合に何が起こるべきかについて定義された動作はありません。

第二の溶液は、未定義の動作(§6.5.5 5)ゼロの結果で除算するので、また、未定義の動作である:

/オペレータの結果は第によって 最初のオペランドの除算から商であります; %演算子の結果は の余りです。両方の演算で、第2オペランドの値が の場合、その動作は未定義です。

未定義の動作には、プログラムの「クラッシュ」が含まれる場合もあれば、含まれない場合もあります。

2

puts()への引数の型がconst char *で、 "読み取り専用のポインタchar"を意味するため、コードが失敗しています。

これは静的で、他のものを渡そうとしただけでは変更されません。その代わりに、関数は引数値を文字のポインタと解釈します(コンパイラはそれをコンパイルしてもintの値がconst char *に変換されないため、厳しい前提です。

2のような小さい整数は、デスクトップ/サーバクラスのシステム上のポインタとしては有効ではありません(また、すべての埋め込み型システムではありません)。つまり、そのアドレスで一般的なプロセスで使用できるメモリがないため、オペレーティングシステムがプロセスの境界を超えてプロセスを停止することが起こります。しかし、コメントに記載されているように、この部分は未定義です。

+1

渡されたポインタは 'const'である必要はありません(Cには「読み取り専用」という概念はありません)。 'puts'がpointeeを変更しないことを保証します。 "一般的に、2のような小さな整数はポインタとして有効ではありません" - 非常に危険な仮定。ほとんどのシステムは組み込みシステムであり、これらのより低いアドレス値の大部分は有効です。これを、Linux、WindowsなどのフルサイズのOSに制限する方がよい。 – Olaf

3

そして、あなたの2つ目の質問に答えるために:

main(i){i=puts("Goodbye Cruel World!")/0;} 

を最初のものはNUL文字ですが、二番目はゼロによる除算である '\0'間と/0違いがあります。したがって、このコードはputsの結果をゼロで除算しようとします。

関連する問題