7

私は本当にこれについて助けが必要です.C.Longの私の基盤を揺るがしました.詳細な回答は非常に高く評価されます.私は私の質問を2つの部分に分けました.

A:従来と同じようにprintf("%s",(char[]){'H','i','\0'});動作して出力するのはなぜですか?C コードのどこでも代用として使用できますか?それらは同じ意味ですか?つまり、C で記述する場合、通常、メモリ内のどこかに格納されていることを意味します。それへのポインターが渡されます。一見醜いものについても同じことが言えますか?それらはまったく同じですか?Hiprintf("%s","Hi");(char[]){'H','i','\0'}"Hi""Hi"Hi(char[]){'H','i','\0'}

B:printf("%s",(char[]){'H','i','\0'})正常に動作する場合と同様に、警告にもかかわらず実行すると、大きな失敗やセグメンテーション違反が発生するのprintf("%s","Hi")はなぜですか? Cでは、関数の引数で渡すときのように、に分解することは想定されてprintf("%s",(char*){'A','B','\0'}いないので、私はただ驚いています。と同じ関数?なぜ結果はここで同じではないのですか?char[]char*char*char demo[]char demo*

これについて私を助けてください.CIの非常に基本的なことをまだ理解していないように感じます.非常に失望しています.ありがとう!!

4

5 に答える 5

3

(わかりました...誰かが質問を完全に作り直しました。答えを作り直しました。)

#3 配列には 16 進バイトが含まれています。(その4番目のものについては知りません):

48 49 00 xx

その配列の内容を渡すとき、2 番目のケースでのみ、それらのバイトを印刷する文字列のアドレスとして使用します。これらの 4 バイトが実際の CPU ハードウェアでどのようにポインターに変換されるかによって異なりますが、「414200FF」がアドレスであるとしましょう (4 番目のバイトは 0xFF であると推測するためです。とにかく、これをすべて作成しています。)また、ポインターの長さは4バイトで、エンディアン順なども想定しています。答えには関係ありませんが、他の人は自由に説明できます。

:他の回答の1つは、0x48を取り、それを(int)0x00000048に拡張し、それをポインターと呼ぶと考えているようです。になり得る。しかし、GCCがそれを行い、@KiethThompsonが生成されたコードをチェックしたとは言わなかったとしても、他のCコンパイラが同じことをするという意味ではありません。結果はどちらでも同じです。

これは printf() 関数に渡され、そのアドレスに移動して印刷する文字を取得しようとします。(そのアドレスがマシン上に存在しない可能性があり、とにかく読み取り用のプロセスに割り当てられていないため、セグフォルトが発生します。)

ケース#2では、ポインターではなく配列であることを認識しているため、バイトが格納されているメモリのアドレスを渡し、printf()でそれを実行できます。

より正式な言語については、他の回答を参照してください。

考慮すべきことの 1 つは、少なくとも一部の C コンパイラは、おそらくprintf他の関数の呼び出しからの呼び出しを認識していないということです。したがって"format string"、呼び出しのポインター (たまたま文字列へのポインター) を取得して格納し、次に 2 番目のパラメーターを取得して、関数の宣言に従って取得したものをすべて格納しintますchar。電話。次に、関数は、同じ宣言に従って、呼び出し元が配置した場所からこれらを引き出します。2 番目以降のパラメーターの宣言は、ポインター、int、double、および存在する可能性のあるすべての異なる型を受け入れることができるように、本当に汎用的なものにする必要があります。(私が言っているのは、2 番目以降のパラメーターをどうするかを決定するときに、コンパイラーはおそらくフォーマット文字列を見ないということです。)

何が起こるかを見るのは興味深いかもしれません:

printf("%s",{'H','i','\0'});
printf("%s",(char *)(char[]){'H','i','\0'}); // This works according to @DanielFischer

予測?

于 2013-05-17T17:20:40.630 に答える
2

いずれの場合も、コンパイラは char[3] 型の初期化されたオブジェクトを作成します。最初のケースでは、オブジェクトを配列として扱うため、最初の要素へのポインターを関数に渡します。2 番目のケースでは、オブジェクトをポインターとして扱うため、オブジェクトの値を渡します。printf はポインターを予期しており、ポインターとして扱われるとオブジェクトの値が無効になるため、実行時にプログラムがクラッシュします。

于 2013-05-17T17:22:19.400 に答える