char* で入力したデータを上書き

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
mrs

char* で入力したデータを上書き

#1

投稿記事 by mrs » 13年前

題名分かりにくくてすいません。。。とりあえずコード書いてみます。

コード:

char* myname;
myname = "さいとう";
//中略(任意のコード)
myname = "じゅげむじゅげむごこうのすりきれかいじゃりすいぎょうんぬん";
このようなコードに関して質問です。

char* は保存先のアドレスを指定するだけであって、文字列の保存先に与えられているデータ長は明示されていません。
そういうことを考えると、例えば「さいとう」のような短い文字列をインプットした後に「じゅげむじゅげむ・・・」のような極めて長い文字列をインプットしようとすると、
元々文字列の保存先に与えられているメモリの領域を超えて、予期しないアドレスへの上書きが生じてしまうのでしょうか。
それとも、長い文字列をインプットしようとすると、自動的に適切なメモリ領域を再配分してくれるのでしょうか。

そもそも自分の知識が根本的に間違っているかもしれませんが、上のコードはどのような場合でも誤作動なく動作するものなのかどうかが知りたいです。
どうぞよろしくお願いします。

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: char* で入力したデータを上書き

#2

投稿記事 by bitter_fox » 13年前

mrs さんが書きました: char* は保存先のアドレスを指定するだけであって、文字列の保存先に与えられているデータ長は明示されていません。
そういうことを考えると、例えば「さいとう」のような短い文字列をインプットした後に「じゅげむじゅげむ・・・」のような極めて長い文字列をインプットしようとすると、
元々文字列の保存先に与えられているメモリの領域を超えて、予期しないアドレスへの上書きが生じてしまうのでしょうか。
それとも、長い文字列をインプットしようとすると、自動的に適切なメモリ領域を再配分してくれるのでしょうか。
C言語では再配分してくれないので予期しないアドレスへの書き込んでしまう恐れがあります。
ただ、新しい文字列の長さが分かっている場合はstrlen関数を用いて自前で新たに確保すれば問題ありません。(ヌル文字に注意)
http://www9.plala.or.jp/sgwr-t/lib/strlen.html
http://www1.cts.ne.jp/~clab/hsample/Point/Point03.html
mrs さんが書きました:

コード:

char* myname;
myname = "さいとう"; // (*1)
//中略(任意のコード)
myname = "じゅげむじゅげむごこうのすりきれかいじゃりすいぎょうんぬん"; // (*2)
そもそも自分の知識が根本的に間違っているかもしれませんが、上のコードはどのような場合でも誤作動なく動作するものなのかどうかが知りたいです。
どうぞよろしくお願いします。
上のコードでは上記のような問題は発生しません。
なぜなら、書き換えているのはポインタの指すアドレスの先の文字列ではなくポインタの指すアドレスを書き換えています。
つまり(*1)が実行されるとmynameの指すアドレスは"さいとう"の先頭アドレスで、
(*2)が実行されるとmynameの指すアドレスは"じゅげむじゅげむごこうのすりきれかいじゃりすいぎょうんぬん"の先頭アドレスになります。
実際の文字列("さいとう")は変化していません。

上記のような問題が起こるのはポインタの指すアドレスの先の文字列を書き換えるような関数(scanf, strcpy, ...)を使った時です。

ちなみに、(*1)のあとでmyname[0] = 'a'やstrcpy(myname, "aa")など領域を超えない範囲で代入・変更を行ってもアプリケーションエラーになってしまう可能性があります。(未定義の動作)
これは、"さいとう"などは文字列リテラルと呼ばれメモリの特殊な場所に確保されるためです。

mrs

Re: char* で入力したデータを上書き

#3

投稿記事 by mrs » 13年前

なるほど!
例に示したコードの場合、文字列の先頭アドレスそのものが全然違う場所に移動して、
その際に新しく入力した文字列の長さ分のメモリ領域も改めて正しく確保してくれるんですね。

ところで、ご回答頂いた内容に関して改めて質問したいのですが、
(*2)の時点では、最初に代入した「さいとう」の文字列はメモリのどこかに残ったままということでしょうか。
そして、この「さいとう」分のメモリ情報は何らかの方法で開放した方がよいのでしょうか。

よろしくお願いします。

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: char* で入力したデータを上書き

#4

投稿記事 by bitter_fox » 13年前

mrs さんが書きました:なるほど!
例に示したコードの場合、文字列の先頭アドレスそのものが全然違う場所に移動して、
その際に新しく入力した文字列の長さ分のメモリ領域も改めて正しく確保してくれるんですね。

ところで、ご回答頂いた内容に関して改めて質問したいのですが、
(*2)の時点では、最初に代入した「さいとう」の文字列はメモリのどこかに残ったままということでしょうか。
そして、この「さいとう」分のメモリ情報は何らかの方法で開放した方がよいのでしょうか。
確保されるタイミングはプログラムがメモリ上に展開された時です。ですから(*1)の時点ですでに"じゅげむじゅげむごこうのすりきれかいじゃりすいぎょうんぬん"も存在しています。
また、プログラムが終了すれば自動的に解放されます。

この領域をプログラマが解放することはできません。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: char* で入力したデータを上書き

#5

投稿記事 by softya(ソフト屋) » 13年前

一般的なコンパイラでは、プログラムコードの静的領域に両文字列とも格納されています。
この場合にプログラム実行時に行われることは、文字列を指し示すmyname のポインタ値を書き換えることだけですね。

>その際に新しく入力した文字列の長さ分のメモリ領域も改めて正しく確保してくれるんですね。
コンパイル時に確定しているので、実行時にはそんな動作は起こりません。

>(*2)の時点では、最初に代入した「さいとう」の文字列はメモリのどこかに残ったままということでしょうか。
>そして、この「さいとう」分のメモリ情報は何らかの方法で開放した方がよいのでしょうか。

実行中に静的領域は破棄することも変更することも出来ません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

mrs

Re: char* で入力したデータを上書き

#6

投稿記事 by mrs » 13年前

お二方ともありがとうございます。
自分にもなんとなく状況が分かって来ました。

代入予定の文字列は全てコンパイル時に確定しており、
プログラムを実行した直後、それらの文字列はメモリ上に全て配置されるということですね。
ところで、少し疑問に思ったのは以下の場合です。

コード:

char* myname;
for(i=0; i<100; i++) myname = "さいとう";
この場合、メモリ上に「さいとう」の文字列が100個格納されるのでしょうか、
それとも1個で済むのでしょうか。
また、

コード:

char* myname;
myname = "さいとう";
myname = "さいとう";
この場合、同様に2個格納されるのか、
それとも同じ文字列の場合はそれをコンパイラが認識して1個にまとめるのか、
という点も気になります。

度々質問してしまいますが、よろしくお願いします。

naohiro19
記事: 256
登録日時: 14年前
住所: 愛知県

Re: char* で入力したデータを上書き

#7

投稿記事 by naohiro19 » 13年前

残念ながら明示されたプログラムをgccでコンパイルすると
deprecated conversion from string constant to 'char*'
文字列定数は char*へ変換は無視されました。
と警告されます

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: char* で入力したデータを上書き

#8

投稿記事 by softya(ソフト屋) » 13年前

私のところgcc 4.3.4ではエラーが出ませんでした。エラーレベルの違い?

コード:

char* myname;
int i;
for(i=0; i<100; i++) myname = "さいとう";
これはコンパイラから見ると "さいとう"は一度しかでてこないので、1つ確保します。
コンパイルと実行を混同されていると思います。

コード:

char* myname;
myname = "さいとう";
myname = "さいとう";
超手抜きなコンパイラを作れば別ですが、同じ文字列を2つ定数として持つコンパイラはありません。
自動的に一つに統合されます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: char* で入力したデータを上書き

#9

投稿記事 by ISLe » 13年前

文字列リテラルはconst char[]なので、char*にアドレスを代入しようとすると警告されるわけです。
多くの環境では文字列リテラルを書き換えようとするとプログラムが強制停止するのですが、ウィンドウズでは文字列リテラルを書き換えることが可能です。
おそらくMS-DOS時代からの名残でしょうね。

mrs
記事: 5
登録日時: 13年前

Re: char* で入力したデータを上書き

#10

投稿記事 by mrs » 13年前

>これはコンパイラから見ると "さいとう"は一度しかでてこないので、1つ確保します。
>コンパイルと実行を混同されていると思います。
自分のコンパイルに関する知識が不足していました。
機械語に変換する際にループ処理を展開するようなことは(少なくともfor文に関しては)ないということですね。
仮に展開されていたとしても、2つめのサンプルコードに関するソフト屋さんのご回答を考慮すれば、
いずれにしても「さいとう」の確保数は1つということになりそうです。

>超手抜きなコンパイラを作れば別ですが、同じ文字列を2つ定数として持つコンパイラはありません。
>自動的に一つに統合されます。
なるほど、その辺りはちゃんとうまくやってくれるんですね。すばらしい。

何度も質問してしまいましたが、とても参考になりました。
回答して頂いた方々ありがとうございました。
また機会がありましたら是非ともよろしくお願いします。
しゅみでげーむつくってます(しかしえがちめいてきにへたです)
■ばぐはおれをほんろうする。が、おれをそだててもくれている・・・のかもしれない。

閉鎖

“C言語何でも質問掲示板” へ戻る