scanfで文字列を読み取る


147

私は何かについて少し混乱しています。Cの文字列を正しく読み取る方法はscanf()

(バッファオーバーフローの可能性を気にしないでください。これは単なる例です)

char string[256];
scanf( "%s" , string );

ただし、以下も機能するようです。

scanf( "%s" , &string );

これは私のコンパイラ(gcc)、純粋な運、または何か他のものですか?


2番目のケースでは、バッファをまったく使用していないため、実際にはバッファオーバーフローの可能性はありません。それとも、または3文字を超える文字列は「バッファ」をオーバーフローすると言うことができます。
TED

最初の例を参考にしました。また、ここで何が起こっているのかをすでに指摘している人もいます。
abeln '23年

うん。それを試してみました、そしてガレスは正しいです。変だ。
TED 2011年

この質問は「scanfを使用して文字列を読み取る方法」を検索すると返されるため、この質問はバッファへのポインタをに渡す方法scanfに関するものであり、質問と受け入れられた回答の両方がそして、実際のコードで使用する必要がある最大入力長の非常に重要な制限を省略します(ただし、この質問の要点は別です)。
Arkku 2018

回答:


141

配列scanf("%s", string)は最初の要素へのポインタに「減衰」するため、と同等scanf("%s", &string[0])です。一方、へscanf("%s", &string)のポインタを渡しますchar[256]が、同じ場所を指しています。

次にscanf、その引数リストの末尾を処理するときに、を引き出しchar *ます。これは、stringまたはを渡したときの正しい&string[0]ことですが、渡した&stringときは、言語標準が保証していないもの、つまりポインタ&string&string[0] -異なるタイプとサイズのオブジェクトへのポインタにます同じ場所から開始します-同じ方法で表されます。

これが機能しないシステムに出会ったことはないと思います。実際には、おそらく安全です。それでもなお、それは間違っており、一部のプラットフォームでは失敗する可能性があります。(架空の例:すべてのポインターに型情報を含む「デバッグ」実装。私は考えるシンボリック「Lispマシン」のC実装は、このような何かをしました。)


5
+1。(他の回答が誤って主張するように、ランダムなメモリ破損を引き起こす代わりに)配列の減衰&stringが同じように機能することを確認することも簡単stringです:printf("%x\n%x\n", string, &string);
Josh Kelley

@Josh Kelley興味深いですが、malloc()を介して割り当てられた文字列へのポインタではこれが当てはまらないと思いますか?
abeln '23年

4
@abeln:正解です。malloc()によって割り当てられた文字列の場合、stringはポインタであり&string、そのポインタのアドレスであるため、2つは交換可能ではありません。ガレスとしても、配列の崩壊の場合のために、説明、stringおよび&string技術的に同じ種類(彼らはほとんどのアーキテクチャ用の交換することが起こるにもかかわらず)ではない、とあなたがオンならばgccがあなたの第二の例のための警告を与えます-Wall
ジョシュケリー

@ジョシュ・ケリー:ありがとう、私はこれについて私の心をクリアできてよかったです。
abeln '23年

1
@JCooper str、&str [0]はどちらも同じもの(配列の先頭)を表しますが、必ずしも&strが同じメモリアドレスである必要はありません。strPtrがstrを指すようになったため、strPtr内に格納されているメモリアドレスはstrおよびthats 12fe60と同じになります。最後に&strPtrは変数strPtrのアドレスです。これはstrptrに格納されている値ではなく、strPtrの実際のメモリアドレスです。strptrは他の変数とは異なる変数であるため、メモリアドレスも異なります。この場合、12fe54
Juan Besa

-9

以下はこれで正確であり、役立つと思います。エラーが見つかった場合は、自由に修正してください。私はCの新人です

char str[]  
  1. char型の値の配列、メモリ内に独自のアドレス
  2. char型の値の配列。メモリ内の独自のアドレスは、配列の要素と同じ数の連続したアドレスです。
  3. 終端のnull文字を含む'\0' &str&str[0]およびstr、3つすべてが配列の最初の要素のアドレスであるメモリ内の同じ場所を表すstr

    char * strPtr =&str [0]; //宣言と初期化

または、これを2つに分割することもできます。

char *strPtr; strPtr = &str[0];
  1. strPtr へのポインタです char
  2. strPtr 配列の点 str
  3. strPtr メモリ内に独自のアドレスを持つ変数です
  4. strPtr アドレスの値を格納する変数です &str[0]
  5. strPtr メモリ内の自身のアドレスは、それが格納するメモリアドレスとは異なります(メモリ内の配列のアドレス、つまり&str [0])
  6. &strPtr strPtr自体のアドレスを表します

ポインタへのポインタを次のように宣言できると思います。

char **vPtr = &strPtr;  

strPtrポインタのアドレスで宣言および初期化します

または、2つに分割することもできます。

char **vPtr;
*vPtr = &strPtr
  1. *vPtr strPtrポインターを指す
  2. *vPtr メモリ内に独自のアドレスを持つ変数です
  3. *vPtr アドレス&strPtrの値を格納する変数です
  4. 最終コメント:できませんstr++str住所はですが、constできますstrPtr++
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.