題名分かりにくくてすいません。。。とりあえずコード書いてみます。
このようなコードに関して質問です。
char* は保存先のアドレスを指定するだけであって、文字列の保存先に与えられているデータ長は明示されていません。
そういうことを考えると、例えば「さいとう」のような短い文字列をインプットした後に「じゅげむじゅげむ・・・」のような極めて長い文字列をインプットしようとすると、
元々文字列の保存先に与えられているメモリの領域を超えて、予期しないアドレスへの上書きが生じてしまうのでしょうか。
それとも、長い文字列をインプットしようとすると、自動的に適切なメモリ領域を再配分してくれるのでしょうか。
そもそも自分の知識が根本的に間違っているかもしれませんが、上のコードはどのような場合でも誤作動なく動作するものなのかどうかが知りたいです。
どうぞよろしくお願いします。
char* で入力したデータを上書き
- bitter_fox
- 記事: 607
- 登録日時: 14年前
- 住所: 大阪府
Re: char* で入力したデータを上書き
C言語では再配分してくれないので予期しないアドレスへの書き込んでしまう恐れがあります。mrs さんが書きました: char* は保存先のアドレスを指定するだけであって、文字列の保存先に与えられているデータ長は明示されていません。
そういうことを考えると、例えば「さいとう」のような短い文字列をインプットした後に「じゅげむじゅげむ・・・」のような極めて長い文字列をインプットしようとすると、
元々文字列の保存先に与えられているメモリの領域を超えて、予期しないアドレスへの上書きが生じてしまうのでしょうか。
それとも、長い文字列をインプットしようとすると、自動的に適切なメモリ領域を再配分してくれるのでしょうか。
ただ、新しい文字列の長さが分かっている場合はstrlen関数を用いて自前で新たに確保すれば問題ありません。(ヌル文字に注意)
http://www9.plala.or.jp/sgwr-t/lib/strlen.html
http://www1.cts.ne.jp/~clab/hsample/Point/Point03.html
上のコードでは上記のような問題は発生しません。
なぜなら、書き換えているのはポインタの指すアドレスの先の文字列ではなくポインタの指すアドレスを書き換えています。
つまり(*1)が実行されるとmynameの指すアドレスは"さいとう"の先頭アドレスで、
(*2)が実行されるとmynameの指すアドレスは"じゅげむじゅげむごこうのすりきれかいじゃりすいぎょうんぬん"の先頭アドレスになります。
実際の文字列("さいとう")は変化していません。
上記のような問題が起こるのはポインタの指すアドレスの先の文字列を書き換えるような関数(scanf, strcpy, ...)を使った時です。
ちなみに、(*1)のあとでmyname[0] = 'a'やstrcpy(myname, "aa")など領域を超えない範囲で代入・変更を行ってもアプリケーションエラーになってしまう可能性があります。(未定義の動作)
これは、"さいとう"などは文字列リテラルと呼ばれメモリの特殊な場所に確保されるためです。
Re: char* で入力したデータを上書き
なるほど!
例に示したコードの場合、文字列の先頭アドレスそのものが全然違う場所に移動して、
その際に新しく入力した文字列の長さ分のメモリ領域も改めて正しく確保してくれるんですね。
ところで、ご回答頂いた内容に関して改めて質問したいのですが、
(*2)の時点では、最初に代入した「さいとう」の文字列はメモリのどこかに残ったままということでしょうか。
そして、この「さいとう」分のメモリ情報は何らかの方法で開放した方がよいのでしょうか。
よろしくお願いします。
例に示したコードの場合、文字列の先頭アドレスそのものが全然違う場所に移動して、
その際に新しく入力した文字列の長さ分のメモリ領域も改めて正しく確保してくれるんですね。
ところで、ご回答頂いた内容に関して改めて質問したいのですが、
(*2)の時点では、最初に代入した「さいとう」の文字列はメモリのどこかに残ったままということでしょうか。
そして、この「さいとう」分のメモリ情報は何らかの方法で開放した方がよいのでしょうか。
よろしくお願いします。
- bitter_fox
- 記事: 607
- 登録日時: 14年前
- 住所: 大阪府
Re: char* で入力したデータを上書き
確保されるタイミングはプログラムがメモリ上に展開された時です。ですから(*1)の時点ですでに"じゅげむじゅげむごこうのすりきれかいじゃりすいぎょうんぬん"も存在しています。mrs さんが書きました:なるほど!
例に示したコードの場合、文字列の先頭アドレスそのものが全然違う場所に移動して、
その際に新しく入力した文字列の長さ分のメモリ領域も改めて正しく確保してくれるんですね。
ところで、ご回答頂いた内容に関して改めて質問したいのですが、
(*2)の時点では、最初に代入した「さいとう」の文字列はメモリのどこかに残ったままということでしょうか。
そして、この「さいとう」分のメモリ情報は何らかの方法で開放した方がよいのでしょうか。
また、プログラムが終了すれば自動的に解放されます。
この領域をプログラマが解放することはできません。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 14年前
- 住所: 東海地方
- 連絡を取る:
Re: char* で入力したデータを上書き
一般的なコンパイラでは、プログラムコードの静的領域に両文字列とも格納されています。
この場合にプログラム実行時に行われることは、文字列を指し示すmyname のポインタ値を書き換えることだけですね。
>その際に新しく入力した文字列の長さ分のメモリ領域も改めて正しく確保してくれるんですね。
コンパイル時に確定しているので、実行時にはそんな動作は起こりません。
>(*2)の時点では、最初に代入した「さいとう」の文字列はメモリのどこかに残ったままということでしょうか。
>そして、この「さいとう」分のメモリ情報は何らかの方法で開放した方がよいのでしょうか。
実行中に静的領域は破棄することも変更することも出来ません。
この場合にプログラム実行時に行われることは、文字列を指し示すmyname のポインタ値を書き換えることだけですね。
>その際に新しく入力した文字列の長さ分のメモリ領域も改めて正しく確保してくれるんですね。
コンパイル時に確定しているので、実行時にはそんな動作は起こりません。
>(*2)の時点では、最初に代入した「さいとう」の文字列はメモリのどこかに残ったままということでしょうか。
>そして、この「さいとう」分のメモリ情報は何らかの方法で開放した方がよいのでしょうか。
実行中に静的領域は破棄することも変更することも出来ません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: char* で入力したデータを上書き
お二方ともありがとうございます。
自分にもなんとなく状況が分かって来ました。
代入予定の文字列は全てコンパイル時に確定しており、
プログラムを実行した直後、それらの文字列はメモリ上に全て配置されるということですね。
ところで、少し疑問に思ったのは以下の場合です。
この場合、メモリ上に「さいとう」の文字列が100個格納されるのでしょうか、
それとも1個で済むのでしょうか。
また、
この場合、同様に2個格納されるのか、
それとも同じ文字列の場合はそれをコンパイラが認識して1個にまとめるのか、
という点も気になります。
度々質問してしまいますが、よろしくお願いします。
自分にもなんとなく状況が分かって来ました。
代入予定の文字列は全てコンパイル時に確定しており、
プログラムを実行した直後、それらの文字列はメモリ上に全て配置されるということですね。
ところで、少し疑問に思ったのは以下の場合です。
この場合、メモリ上に「さいとう」の文字列が100個格納されるのでしょうか、
それとも1個で済むのでしょうか。
また、
この場合、同様に2個格納されるのか、
それとも同じ文字列の場合はそれをコンパイラが認識して1個にまとめるのか、
という点も気になります。
度々質問してしまいますが、よろしくお願いします。
Re: char* で入力したデータを上書き
残念ながら明示されたプログラムをgccでコンパイルすると
と警告されますdeprecated conversion from string constant to 'char*'
文字列定数は char*へ変換は無視されました。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 14年前
- 住所: 東海地方
- 連絡を取る:
Re: char* で入力したデータを上書き
私のところgcc 4.3.4ではエラーが出ませんでした。エラーレベルの違い?
これはコンパイラから見ると "さいとう"は一度しかでてこないので、1つ確保します。
コンパイルと実行を混同されていると思います。
超手抜きなコンパイラを作れば別ですが、同じ文字列を2つ定数として持つコンパイラはありません。
自動的に一つに統合されます。
これはコンパイラから見ると "さいとう"は一度しかでてこないので、1つ確保します。
コンパイルと実行を混同されていると思います。
超手抜きなコンパイラを作れば別ですが、同じ文字列を2つ定数として持つコンパイラはありません。
自動的に一つに統合されます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: char* で入力したデータを上書き
文字列リテラルはconst char[]なので、char*にアドレスを代入しようとすると警告されるわけです。
多くの環境では文字列リテラルを書き換えようとするとプログラムが強制停止するのですが、ウィンドウズでは文字列リテラルを書き換えることが可能です。
おそらくMS-DOS時代からの名残でしょうね。
多くの環境では文字列リテラルを書き換えようとするとプログラムが強制停止するのですが、ウィンドウズでは文字列リテラルを書き換えることが可能です。
おそらくMS-DOS時代からの名残でしょうね。
Re: char* で入力したデータを上書き
>これはコンパイラから見ると "さいとう"は一度しかでてこないので、1つ確保します。
>コンパイルと実行を混同されていると思います。
自分のコンパイルに関する知識が不足していました。
機械語に変換する際にループ処理を展開するようなことは(少なくともfor文に関しては)ないということですね。
仮に展開されていたとしても、2つめのサンプルコードに関するソフト屋さんのご回答を考慮すれば、
いずれにしても「さいとう」の確保数は1つということになりそうです。
>超手抜きなコンパイラを作れば別ですが、同じ文字列を2つ定数として持つコンパイラはありません。
>自動的に一つに統合されます。
なるほど、その辺りはちゃんとうまくやってくれるんですね。すばらしい。
何度も質問してしまいましたが、とても参考になりました。
回答して頂いた方々ありがとうございました。
また機会がありましたら是非ともよろしくお願いします。
>コンパイルと実行を混同されていると思います。
自分のコンパイルに関する知識が不足していました。
機械語に変換する際にループ処理を展開するようなことは(少なくともfor文に関しては)ないということですね。
仮に展開されていたとしても、2つめのサンプルコードに関するソフト屋さんのご回答を考慮すれば、
いずれにしても「さいとう」の確保数は1つということになりそうです。
>超手抜きなコンパイラを作れば別ですが、同じ文字列を2つ定数として持つコンパイラはありません。
>自動的に一つに統合されます。
なるほど、その辺りはちゃんとうまくやってくれるんですね。すばらしい。
何度も質問してしまいましたが、とても参考になりました。
回答して頂いた方々ありがとうございました。
また機会がありましたら是非ともよろしくお願いします。
しゅみでげーむつくってます(しかしえがちめいてきにへたです)
■ばぐはおれをほんろうする。が、おれをそだててもくれている・・・のかもしれない。
■ばぐはおれをほんろうする。が、おれをそだててもくれている・・・のかもしれない。