問題は、first
ポインタ変数が初期化されていないことです。つまり、ポインタが任意の値を持ち、メモリ内の任意の場所を指している可能性があります。
運が良ければ、未使用の割り当てられたメモリを指すことになるため、文字列の読み取りはたまたま機能します。
運が良ければ、マップされていないアドレスを指すことになるため、文字列を読み取るとセグメンテーション違反が発生します。
運が悪ければ、何か他のものを保持する有効なメモリを指してしまうため、機能しているように見えますが、他のデータ (またはコード) を上書きし、デバッグが困難な不可解なクラッシュ、誤った結果、またはセキュリティ ホールを引き起こします。
追加int i = 0;
しても、「サイコロを再ロールする」ことを除いて、実際には何も変わりません。また、たとえばコンパイラ フラグを変更することによって、異なる結果を得ることができます (特に、デバッグ機能または最適化機能をオンまたはオフにした場合)。
main
たとえば、関数に入ると、スタックが割り当てられる領域は次のようになります。
pointer to return address in the middle of libc
pointer to data segment
0
コードの最初のバージョンでは、何も初期化せずfirst
、データ セグメントへのポインターの値を継承することになるため、データ セグメントへのスキャンが機能します。2 番目のバージョンでi
は、データ セグメントへのポインターを継承する (そして で上書きする0
)ことになり、値をfirst
継承する0
ことになるため、セグメンテーション違反が発生します。
実際に何が起こっているのかを確認したい場合は、-S
フラグ (またはコンパイラの同等のもの)によって生成されたアセンブリをprintf("%p\n", first)
確認するか、取得したアドレスを確認して、そこにマップされているものを把握することができます。
しかし、実際には、なぜ機能しないかは問題ではありません。動作するはずがありません。唯一の解決策は、ポインターを有効なものに適切に初期化することです(ouahの回答と他の人が説明しているように)。