質問のコードを繰り返す:
char c[10];
char* d;
1.1。
scanf("%s", &c);
printf("%s\n", &c);
これは期待どおりに機能する可能性がありますが、実際の動作は未定義です。
scanf
フォーマットを使用する"%s"
には、タイプの引数が必要char*
です。 &c
タイプchar (*)[10]
は、つまり、char[10]
配列へのポインタです。の0番目の要素のアドレスと同じメモリ内の場所を指しますが、c
タイプが異なります。同じことがprintf
::"%s"
形式でも引数を期待するように指示されchar*
ますが、引数を渡しchar(*)[10]
ます。
は可変個引数関数であるためscanf
、フォーマット文字列以外の引数の型チェックは必要ありません。コンパイラーは、値を処理できると仮定して、(おそらく)char (*)[10]
値をに喜んで渡します。scanf
そして、すべてのポインターが同じサイズ、表現、および引数受け渡しメカニズムを持つ実装では、おそらく可能です。char*
ただし、たとえば、エキゾチックアーキテクチャ用のCコンパイラは、ポインタをより大きな型へのポインタよりも簡単に大きくすることができます。たとえば、ネイティブアドレスが64ビットワードを指しているCPUを想像してみてください。char*
ポインタは、ワードポインタとバイトオフセットで構成されている場合があります。
2.2。
scanf("%s", c);
printf("%s\n", c);
これの方が良い。 c
は配列ですが、このコンテキストでは、配列式は配列の最初の要素へのポインタに「減衰」します。これはscanf
、"%s"
フォーマットで必要なものとまったく同じです。同じことがに渡さc
れprintf
ます。(しかし、まだいくつかの問題があります。他の例の後でそれについて説明します。
3.3。
scanf("%s", &d);
printf("%s\n", &d);
d
は単一のchar*
引数で&d
あり、型であるためchar**
、ここでも、間違った型の引数を渡しています。すべてのポインタが同じ表現(および同じ引数受け渡しメカニズム)を持ち、の入力がscanf
十分に短い場合、これは「機能する」可能性があります。char*
オブジェクトをの配列であるかのように扱いますchar
。が4バイトで、入力文字列の長さが3文字以下の場合char*
、これはおそらく機能します。つまり、を使用しchar[4]
て呼び出しを正しく記述したかのように機能します。しかし、それは非常に文字列をポインタオブジェクトに直接格納するのは不適切な方法であり、オブジェクトの終わりを超えて書き込むリスクが非常に高く、予測できない結果になります。(これらの予測できない結果には、他の目的に使用されていないメモリへの書き込みが含まれます。これは、機能しているように見える可能性があります。これは、未定義の動作の性質です。)
(C標準では、オブジェクトを文字の配列として扱うための特別な許可が与えられていますが、この場合は非常に悪い考えです。)
4.4。
scanf("%s", d);
printf("%s\n", d);
ここではタイプはすべて正しいですがd
、十分に大きな配列を指すように初期化しない限りchar
、見事に失敗する可能性があります(または、さらに悪いことに、「正しく」機能しているように見えます。つまり、微妙なバグがあります。おそらく後で表示されます)。
そして今、私たちは他の問題について上で述べたことに到達します。
たとえば、4ではd
、「十分に大きい」配列を指す必要があると述べました。「十分に大きい」の大きさはどれくらいですか?それに対する答えはありません。 scanf("%s", ...)
長さに上限がない、空白で区切られた文字のシーケンスを読み取ります。たとえば、プログラムを実行してx
キーを押したままにすると、提供したどのバッファーよりも長い入力文字列を提供でき、予測できない結果が発生します(再び未定義の動作)。
scanf
関数の形式"%s"
を安全に使用することはできません(標準の入力ストリームに表示される内容を制御できる環境でプログラムを実行しない限り)。
テキスト入力を読み取る良い方法の1つは、を使用fgets
して一度に1行を読み取り、他の関数を使用して結果を分析することです。 fgets
入力の最大長を指定する必要があります。実際の入力が制限を超えた場合、それは切り捨てられ、後の呼び出しで読み取られるように残されます。ほど便利ではありませんscanf
が、安全に行うことができます。(また、この関数は絶対に使用しないでください。たとえば、安全に使用することはできません。)gets
scanf("%s", ...)
推奨読書:
comp.lang.c FAQのセクション6は、C配列とポインター、およびそれらがどのように関連しているか(および関連していないか)を説明する優れた仕事をしています。セクション12では、C標準I/Oについて説明します。
(この回答が長すぎて申し訳ありません。短くする時間がありませんでした。)