web-dev-qa-db-ja.com

'&'が 'scanf'ステートメントに含まれていない場合はどうなりますか?

私は質問をされたインタビューに行きました:

次のことについてどう思いますか?

int i;
scanf ("%d", i);
printf ("i: %d\n", i);

私は答えた:

  • プログラムは正常にコンパイルされます。
  • 番号が正しく印刷されませんが、クラッシュすることなく最後まで実行されます

私の反応は間違っていた。圧倒されました。

その後、彼らは私を解雇しました:

プログラムがクラッシュし、コアダンプが発生する場合がありました。

理解できませんでしたなぜプログラムがクラッシュするのでしょうか?誰かが私に理由を説明できますか?助けていただければ幸いです。

31
Ashish Ahuja

変数が定義されると、コンパイラはその変数にメモリを割り当てます。

int i;  // The compiler will allocate sizeof(int) bytes for i

上で定義されたiは初期化されておらず、値が不確定です。

iに割り当てられたメモリ位置にデータを書き込むには、変数のアドレスを指定する必要があります。声明

scanf("%d", &i);

ユーザーがintデータをiに割り当てられたメモリ位置に書き込みます。

&iの前に配置されていない場合、scanfは入力データを&iではなくメモリ位置iに書き込もうとします。 iには不確定な値が含まれているため、メモリアドレスの値と同等の値が含まれている可能性や、メモリアドレスの範囲外の値が含まれている可能性があります。

いずれの場合も、プログラムは不規則に動作する可能性があり、未定義の動作につながります。その場合、何かが起こる可能性があります。

45
haccks

未定義の動作を引き起こすためです。 scanf()ファミリーの関数は、"%d"指定子が見つかったときに、整数へのポインターを予期します。ある整数のアドレスとして解釈できる整数を渡していますが、そうではありません。これには、標準で定義された動作はありません。それは確かにコンパイルされます(ただし、いくつかの警告を発行します)が、それは確かに予期しない方法で動作します。

そのままのコードでは、さらに別の問題があります。 i変数は初期化されないため、値が不確定になりますが、未定義の振る舞いのもう1つの理由です。

標準では、他のタイプが予期されていたときに特定のタイプを渡したときに何が起こるかについては何も述べていないことに注意してください。どのタイプを交換しても、それは単に未定義の動作です。ただし、この特定の状況は、ポインターを整数に変換できるため、特別な考慮事項に該当します。ただし、動作は、ポインターに変換し直し、整数型が値を正しく格納できる場合にのみ定義されます。これがコンパイルされる理由ですが、確かに正しく動作しません。

18
Iharob Al Asimi

間違ったタイプのデータを渡した(_int*_が必要ですが、intが渡されます)scanf()に渡されました。これにより、未定義の動作になります。

未定義の振る舞いには何でも起こり得ます。プログラムがクラッシュする場合とクラッシュしない場合があります。

通常の環境では、オペレーティングシステムによって書き込みが許可されていない場所を指す「アドレス」がscanf()に渡されると、プログラムがクラッシュすると思います。 OSがアプリケーションプログラムを終了すると、クラッシュとして監視されます。

10
MikeCAT

他の回答がまだ言及していないことの1つは、一部のプラットフォームではsizeof (int) != sizeof (int*)です。引数が特定の方法で渡された場合*、scanfは別の変数または戻りアドレスの一部を食いつぶす可能性があります。返信先アドレスを変更すると、セキュリティの脆弱性が発生する可能性があります。

*私はアセンブリ言語の専門家ではないので、これを一粒の塩と一緒に取ってください。

10
Functino

プログラムがクラッシュする理由がわかりませんでしたか?誰かが私に理由を説明してもらえますか。助けていただければ幸いです。

多分もう少し適用されます:

_int i = 123;
scanf ("%d", &i);
_

最初のコマンドで、1つの整数値にメモリを割り当て、このメモリブロックに123を書き込みます。この例では、このメモリブロックのアドレスが_0x0000ffff_であるとします。 2番目のコマンドでは、入力を読み取り、scanfは入力をメモリブロック_0x0000ffff_に書き込みます。これは、この変数iの値にアクセス(逆参照)していないためですが、アドレスです。

代わりにコマンドscanf ("%d", i);を使用すると、入力がメモリに書き込まれますaddress _123_(これは、この変数内に格納されている値であるため)。明らかに、それはひどく間違ってクラッシュを引き起こす可能性があります。

5
MarkWatney