人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

【C言語のChar型で終端用の記号\0が抜けているコーディングについて】

以下のシチュエーションで問題を放置したときに発生すると思われる危険と現象を
説明してください(質問者は中途半端な理解です。)。

<シチュエーション>
C言語のChar型で終端用の記号\0が抜けているコーディングを発見しました。

char 変数a[2]
char 変数b[2]

a[0]="a"
a[1]="b"

b[0]="c"
b[1]="d"

となっており、a配列をアウトプットするとabcdとなってしまいます。
これはメモリの位置が連続しているため、連続してb配列の中身も読み込んでしまったものと思いますが
a配列は"ユニークな値20桁以下"という規定なのでabcdの値でも問題ありません。

デバッグモードで無事に動作しているし、そのまま放置することにしました。

End.

ご回答よろしくお願いします。

●質問者: harunoharuno
●カテゴリ:コンピュータ ウェブ制作
✍キーワード:Char C言語 アウトプット コーディング シチュエーション
○ 状態 :終了
└ 回答数 : 5/5件

▽最新の回答へ

1 ● Km1967
●20ポイント

後続の処理がabcdという値を受け取った場合にどうなるかによる

たとえばメモリリークやデータ破壊に繋がるかもしれない

素直に書き直すべき

char 変数a[2] 
char 変数b[2]

a[0]='a'
a[1]='b'

b[0]='c'
b[1]='d'

http://www.grapecity.com/japan/powernews/column/clang/023/page01...

◎質問者からの返答

a配列以降のアドレスに

配置されるデータがb配列の場合はさして問題ではありません。


たとえば、

デバッグモードを解除してビルドしたら

a配列の後ろにC配列(データ内容="abcdefghijklmnopqrstuvwxyz")が続くように

なったりということはあり得るかな?と思っています(この場合ユニークな値20桁以下の条件から外れてしまいます)。

メモリ上の配置ルールしらないのでなんとなく

怖いです。ここら辺についてもう少し詳しくご回答

いただきたくよろしくお願いします。


2 ● gemwell6809
●100ポイント

変数領域のメモリ上の配置は環境依存なので、たまたまそう言う配置になっている…と言う事ができるかと思います。

ですので、デバッグモードを解除したビルドで配置が変るか?もコンパイル環境によって変ります。

別の環境でコンパイルした場合、a配列とb配列の並びが逆になることもあり得ます。


この問題を放置した場合の危険性ですが、

a配列とb配列の並びが維持されていたとしても、b配列が終端されていないので、更に先のメモリをアクセスすることになります。

意図しない文字コードが表示されることが考えられますし、制御コードに相当する値があった場合には表示が崩れることが考えられます。

また、配列のメモリ配置がプログラム・コードのアクセスが許されている領域の末端に配置された場合には許可されない領域にまでアクセスすることになり、メモリ保護機構のある実行環境であれば、例外を出してプログラムが異常終了することも考えられます。

(a配列とb配列の並びが維持されなかった場合も、やはり、意図しない領域をアクセスすることになります。)


ともかく、たまたま動いただけでC言語として保証される書き方ではない…と言えます。


http://www1.cts.ne.jp/~clab/hsample/Point/Point19.html

◎質問者からの返答

そうですよね。

終端を設定していないとどんなデータが読みだされるかわかったもんじゃないですよね。

環境によってもメモリ上での配置はかわるとのことですし対策をとる必要性を感じます。

(本質問は興味による質問なのでしばらく回答受付いたします。追加情報などございましたらまたよろしくお願いします)


3 ● T_SKG
●100ポイント

変数aと変数bが、連続したメモリ上にとられ、さらにその先のメモリが

たまたま、ゼロクリアされたままの未使用だったのでしょうか、\0 に

なっていたのは、運が良かっただけです。


もし、変数b に続くメモリの17byteが使われて中に \0が入ってなければ、

変数aは、20桁を超えます。


それに、今後もコンパイラが、変数aと変数bと運良く\0が入っている領域

をメモリ上に並べて割り当ててくれる保証はありません。


メモリを極力節約するため、union にしてあるとか、余程トリッキーな

コーディングでないかぎり、こんなものは、修正するべきでしょう。


http://www.mapee.jp/cpp/post_31.html

◎質問者からの返答

どうもありがとうございます。

「配列Bの終端でなんでうまく止まるのかな?」と少々疑問に感じていたのですが、

たまたま\0値ということだったのか・・・。

回答していただいている通り修正すべきですね。

(本質問は本日午前8: 00を持って締め切りとします。)


4 ● garyo
●50ポイント

たまたま運良く止まったと考えた方が良いでしょう。

終端を忘れたプログラムの場合、文字列のコピーなどで、終端が見つからないため、ひたすら長い文字列と解釈して、暴走したりすることがあります。

http://q.hatena.ne.jp/answer


5 ● uehaj
●150ポイント ベストアンサー

これらの変数を利用する側が、aやbが?0で終端された文字列(ASCIIZ文字列)であることを期待するなら、これはかなり深刻なバグです。しかし、?0で終端されている事を期待していないなら,何も問題ありません。「?0で終端されている事を期待しない」とは


strncpy(buf, a, 2);


のように、aの長さ(文字数)を指定して処理すれば良いという事です。ただし、aの長さを指定する方法が別途必要ですね。(別の変数を用意する、決め打ちにするなど)


ただ,ご質問のケースでは、aの長さを別に指定してたりしない、?0終端を期待していると思うので、一般には修正した方がいい部類でしょう。


さて、bの次の値が何か、が問題です。「たまたま?0」なのか、必然なのか、「たまたまといいつつ、(あるOS、あるコンパイラ・・など)条件が定まれば必ずそうなる」のか?です。


その観点からは、これらの変数の記憶クラスが何か、が問題になります。記憶クラスとは、staticとかautoとかで指定されるものです。


C言語では、明示的に初期化していないauto変数の初期値は不定です。auto変数とは,関数内で定義したローカル変数のことで、スタック上に配置されるものです。スタックというのは、関数呼び出し毎に割り当て/解放処理が行われて再利用されるので、前回呼ばれた関数が記録した変数や、あるいは同様にスタック上に配置される関数引数や関数の戻り値アドレスの値が得られ、どんな値が得られるか分からないし、実際毎回異なる値が得られることもあるのです。こういう値を「ゴミの値」という呼ぶ事もあります。


しかしながら、逆に言うと静的記憶クラスに属する

については、明示的に初期値を指定していない場合でも、特定の値に初期化されている可能性があります。特に

これらにより、bの次が?0であることを、限定条件下で「あえて」「良くない事と知りつつ」期待してよい場合があるかもしれません。お薦めはしませんが。

// 「ゴミの値」は\0であることは確率的に低いので、質問者の方はおそらく静

// 的変数もしくは大域変数を使われているのでしょうね。

でもそうだとしても、以下は注意した方がいいでしょう。

場合によっては動作しないケースが実際にあるかもしれないという事です。

まー繰り返しになりますが、直すのが無難だとは思います。

URLは関係ありそうなキーワードです。

http://www.google.co.jp/search?hl=ja&client=firefox-a&hs=OVg&rls...

◎質問者からの返答

非常に丁寧な回答どうもありがとうございました。

また、

独学であまり理解できていないメモリ管理についても説明いただきとても参考になりました。

ご推察の通り私が質問に使用したCharの配列は、実際のコーディングではstaticで宣言しているものです。

静的領域のパディング領域は0パディングであることが多いんですね。

今回の対応と致しましては、

できるだけ多くの環境で動作できるよう問題のコードを修正いたします。

参考情報もありがとうございました。リンクの先にメモリの扱い方を

4つに分けて説明しているHPがあり、参考になりました。

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ