(不定期コラム) アドレスの意味と、C言語でありがちなミスを観察してみる例題 / プログラミング1

Share on:

m_2013060811582951b29dd5a1782 m_2013060815265051b2ceaa69125

写真はピーノ・ヴィーノの牛肉とキノコのボラーニャ風手打ちパスタ。パスタ本体はともかく、ともかくこういうソースを作れるようになりたいなー。

プログラミング1でポインタ周りの話に突入したらしい。ポインタ虎の巻とか参考になる情報源もいろいろありますが、代表的な鬼門だということで例題記事を一つ書いてみます。


ということで不定期コラム8回目は「アドレスの意味と、C言語でありがちなミスを観察してみる例題」を眺めてみます。

ソース: address.c

コメントやprintf()が多いので長く見えますが、char 型変数のメモリサイズを確認(step1)し、各変数のアドレスを確認(step2)、値を代入して出力確認(step3)、配列の範囲外にアクセスしてみる(step4)、範囲外も含めてアクセスした箇所のアドレスを再確認(step5)しているだけです。

環境によって実行結果も異なりますが、私の環境では以下のような結果になりました。

step1: char型変数のサイズを確認。
size: c=1

step2: 各変数のアドレスを確認。
address: &c=0x7fff5b7a8487, &d[0]=0x7fff5b7a8485, &d[1]=0x7fff5b7a8486

step3: 素直に各char型変数に値を代入して出力してみる。
char: c=1, d[0]=2, d[1]=3

step4: 用意した配列の外側にアクセスしてみる。
char: c=4, d[0]=2, d[1]=3, d[2]=4

step5: アクセスした変数のアドレスを一通り確認してみる。
address: &c=0x7fff5b7a8487, &d[0]=0x7fff5b7a8485, &d[1]=0x7fff5b7a8486, &d[2]=0x7fff5b7a8487

実行結果だけでは分かりにくいので適宜ソースとにらめっこしながら眺めて欲しいですが、ここで問題なのは step4 で d[2] へ値を保存した結果、変更していないはずの c の値が変わってしまっている(上記赤字箇所)ことです。

ここで確認しておきたい点は次の3点です。
(1) char型変数は1バイトで保存されること。
(2) 配列は連続したアドレス空間(メモリ空間)を確保すること。
(3) 配列確保時の個数を越えてもアクセス自体はできてしまう(アドレスによってはアクセスした時点でプロセスが死んでしまうこともある)こと。
この3点のためC言語プログラミングでは、step4のような「不適切なアクセス方法」が実行出来てしまい、それが原因となって想定外の結果を引き起こしてしまうことが少なくありません。

上記を踏まえて、クイズです。

  • このようなミス(予め確保した領域外へのアクセス)を防ぐ、もしくはミスに気づき易くするためにはどのような方法が考えられるだろうか?
  • char型変数は1バイトで保存されるが、int, float 等他の型では何バイト必要だろうか?
  • (発展課題) メモり空間上はバイト単位で区切る敷居と値を保存する領域が用意されているだけで、どこにも「char型変数を保持している」とか「int型変数を保持している」という情報は残されていない。例えばcahr型変数を1バイト、int型変数を2バイトで保存するとした場合、「連続したメモリ空間上の0x00から始まる2バイトの領域」にchar型変数が2個保存されているのか、int型変数が1個保存されているかを直接区別することはできない。C言語においてはどのように対応しているのだろうか?